ping: Fix handling of IP packet sizes
Ping reads raw IP packets to parse ICMP responses. When reading the IP Header Len (IHL) ping was was taking the value from the provided packet without any validation. This could lead to remotely triggerable stack corruption. Validate the IHL against expected and recieved data sizes when reading from the received packet and when reading any quoted packets from within the ICMP response. Approved by: so Reviewed by: markj, asomers Security: FreeBSD-SA-22:15.ping Security: CVE-2022-23093 Sponsored by: NetApp, Inc. Sponsored by: Klara, Inc. X-NetApp-PR: #77 Differential Revision: https://reviews.freebsd.org/D37195 (cherry picked from commit46d7b45a26
) (cherry picked from commit186f495d4b
)
This commit is contained in:
parent
95499c4561
commit
66c7b53d95
@ -952,6 +952,9 @@ ping(int argc, char *const *argv)
|
||||
warn("recvmsg");
|
||||
continue;
|
||||
}
|
||||
/* If we have a 0 byte read from recvfrom continue */
|
||||
if (cc == 0)
|
||||
continue;
|
||||
#ifdef SO_TIMESTAMP
|
||||
if (cmsg != NULL &&
|
||||
cmsg->cmsg_level == SOL_SOCKET &&
|
||||
@ -1133,8 +1136,10 @@ pr_pack(char *buf, ssize_t cc, struct sockaddr_in *from, struct timespec *tv)
|
||||
struct icmp icp;
|
||||
struct ip ip;
|
||||
const u_char *icmp_data_raw;
|
||||
ssize_t icmp_data_raw_len;
|
||||
double triptime;
|
||||
int dupflag, hlen, i, j, recv_len;
|
||||
int dupflag, i, j, recv_len;
|
||||
uint8_t hlen;
|
||||
uint16_t seq;
|
||||
static int old_rrlen;
|
||||
static char old_rr[MAX_IPOPTLEN];
|
||||
@ -1144,15 +1149,27 @@ pr_pack(char *buf, ssize_t cc, struct sockaddr_in *from, struct timespec *tv)
|
||||
const u_char *oicmp_raw;
|
||||
|
||||
/*
|
||||
* Get size of IP header of the received packet. The
|
||||
* information is contained in the lower four bits of the
|
||||
* first byte.
|
||||
* Get size of IP header of the received packet.
|
||||
* The header length is contained in the lower four bits of the first
|
||||
* byte and represents the number of 4 byte octets the header takes up.
|
||||
*
|
||||
* The IHL minimum value is 5 (20 bytes) and its maximum value is 15
|
||||
* (60 bytes).
|
||||
*/
|
||||
memcpy(&l, buf, sizeof(l));
|
||||
hlen = (l & 0x0f) << 2;
|
||||
memcpy(&ip, buf, hlen);
|
||||
|
||||
/* Check the IP header */
|
||||
/* Reject IP packets with a short header */
|
||||
if (hlen < sizeof(struct ip)) {
|
||||
if (options & F_VERBOSE)
|
||||
warn("IHL too short (%d bytes) from %s", hlen,
|
||||
inet_ntoa(from->sin_addr));
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&ip, buf, sizeof(struct ip));
|
||||
|
||||
/* Check packet has enough data to carry a valid ICMP header */
|
||||
recv_len = cc;
|
||||
if (cc < hlen + ICMP_MINLEN) {
|
||||
if (options & F_VERBOSE)
|
||||
@ -1164,6 +1181,7 @@ pr_pack(char *buf, ssize_t cc, struct sockaddr_in *from, struct timespec *tv)
|
||||
#ifndef icmp_data
|
||||
icmp_data_raw = buf + hlen + offsetof(struct icmp, icmp_ip);
|
||||
#else
|
||||
icmp_data_raw_len = cc - (hlen + offsetof(struct icmp, icmp_data));
|
||||
icmp_data_raw = buf + hlen + offsetof(struct icmp, icmp_data);
|
||||
#endif
|
||||
|
||||
@ -1293,12 +1311,45 @@ pr_pack(char *buf, ssize_t cc, struct sockaddr_in *from, struct timespec *tv)
|
||||
* as root to avoid leaking information not normally
|
||||
* available to those not running as root.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If we don't have enough bytes for a quoted IP header and an
|
||||
* ICMP header then stop.
|
||||
*/
|
||||
if (icmp_data_raw_len <
|
||||
(ssize_t)(sizeof(struct ip) + sizeof(struct icmp))) {
|
||||
if (options & F_VERBOSE)
|
||||
warnx("quoted data too short (%zd bytes) from %s",
|
||||
icmp_data_raw_len, inet_ntoa(from->sin_addr));
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&oip_header_len, icmp_data_raw, sizeof(oip_header_len));
|
||||
oip_header_len = (oip_header_len & 0x0f) << 2;
|
||||
memcpy(&oip, icmp_data_raw, oip_header_len);
|
||||
|
||||
/* Reject IP packets with a short header */
|
||||
if (oip_header_len < sizeof(struct ip)) {
|
||||
if (options & F_VERBOSE)
|
||||
warnx("inner IHL too short (%d bytes) from %s",
|
||||
oip_header_len, inet_ntoa(from->sin_addr));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check against the actual IHL length, to protect against
|
||||
* quoated packets carrying IP options.
|
||||
*/
|
||||
if (icmp_data_raw_len <
|
||||
(ssize_t)(oip_header_len + sizeof(struct icmp))) {
|
||||
if (options & F_VERBOSE)
|
||||
warnx("inner packet too short (%zd bytes) from %s",
|
||||
icmp_data_raw_len, inet_ntoa(from->sin_addr));
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&oip, icmp_data_raw, sizeof(struct ip));
|
||||
oicmp_raw = icmp_data_raw + oip_header_len;
|
||||
memcpy(&oicmp, oicmp_raw, offsetof(struct icmp, icmp_id) +
|
||||
sizeof(oicmp.icmp_id));
|
||||
memcpy(&oicmp, oicmp_raw, sizeof(struct icmp));
|
||||
|
||||
if (((options & F_VERBOSE) && uid == 0) ||
|
||||
(!(options & F_QUIET2) &&
|
||||
|
Loading…
Reference in New Issue
Block a user