Coordinated Disclosure Timeline
- 2021-06-04: Maintainers contacted
- 2021-06-04: Maintainers requested private GitLab issues to be opened to track each bug
- 2021-06-14: Issues were fixed on release 4.6.0
Summary
The library libslirp
contains three uninitialized memory vulnerabilities that may allow an attacker to leak host
memory into a guest
Product
libslirp
Tested Version
4.5.0
and dfe1229fc8f707f76b3f4d09078ab5e9b5817469
on the master
branch.
Details
Issue 1: Invalid pointer initialization in bootp.c:bootp_input (GHSL-2021-078)
The function bootp_input
handles requests for the bootp
protocol from the guest. While processing a udp
packet that is smaller than the size of the bootp_t
structure (548 bytes) it uses memory from outside the working mbuf
buffer. This leads to the leakage of 10 bytes of uninitialized heap memory to the guest.
In the following code snippet the function mtod
is used without verifying the availability of enough bytes to properly cast the mbuf
into a bootp_t
type.
void bootp_input(struct mbuf *m)
{
struct bootp_t *bp = mtod(m, struct bootp_t *);
if (bp->bp_op == BOOTP_REQUEST) {
bootp_reply(m->slirp, bp);
}
}
An attacker that is in control of the guest can issue a small packet that will lead to the leakage of the following fields of the bootp_t
type while preparing a response to the processed packet:
struct bootp_t {
...
uint32_t bp_xid;
...
uint8_t bp_hwaddr[16];
};
The fields bp_xid
and bp_hwaddr
are the only two fields of the incoming packet that are copied into the bootp
reply packet as shown in the following snippet:
rbp->bp_op = BOOTP_REPLY;
// (1)
rbp->bp_xid = bp->bp_xid;
rbp->bp_htype = 1;
rbp->bp_hlen = 6;
// (2)
memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, ETH_ALEN);
If we use a debugger we can inspect the contents of bp->bp_xid
, at (1), and see that they are uninitialized (\xbe
is the pattern that AddressSanitizer uses to initialize allocations):
p/x bp->bp_xid
(const uint32_t) $6 = 0xbebebebe
Again if we use a debugger we can inspect the contents of “bp->bp_hwaddr”, at (2), and see that they are uninitialized as well:
mem read &bp->bp_hwaddr[0] -c 16
0x61b000000844: be be be be be be be be be be be be be be be be ................
Once the reply packet is built, it is sent via “udp_output” which ultimately leads to a call to “slirp_send_packet_all”.
void slirp_send_packet_all(Slirp *slirp, const void *buf, size_t len)
{
ssize_t ret = slirp->cb->send_packet(buf, len, slirp->opaque);
if (ret < 0) {
g_critical("Failed to send packet, ret: %ld", (long)ret);
} else if (ret < len) {
DEBUG_ERROR("send_packet() didn't send all data: %ld < %lu", (long)ret,
(unsigned long)len);
}
}
A quick inspection of the sent packet with a debugger shows that we are indeed leaking memory into the guest:
mem read buf -c 80
0x7ffeefbecca0: ff ff ff ff ff ff 52 55 0a 00 00 01 08 00 45 10 ......RU......E.
0x7ffeefbeccb0: 02 40 00 00 00 00 40 11 6e 9d 0a 00 00 01 ff ff .@....@.n.......
0x7ffeefbeccc0: ff ff 00 43 00 44 02 2c 64 31 02 01 06 00 be be ...C.D.,d1......
0x7ffeefbeccd0: be be 00 00 00 00 00 00 00 00 0a 00 00 03 0a 00 ................
0x7ffeefbecce0: 00 01 00 00 00 00 be be be be be be 00 00 00 00 ................
Impact
This issue may lead to host memory disclosure
.
Issue 2: Invalid pointer initialization in udp6.c:udp6_input (GHSL-2021-079)
The function udp6_input
handles requests for the udp
protocol from the guest. While processing a udp
packet that is smaller than the size of the udphdr
structure it uses memory from outside the working mbuf
buffer.
In the following code snippet the function mtod
is used without verifying the availability of enough bytes to properly cast the mbuf
into a udphdr
type.
void udp6_input(struct mbuf *m)
{
Slirp *slirp = m->slirp;
struct ip6 *ip, save_ip;
struct udphdr *uh;
int iphlen = sizeof(struct ip6);
int len;
struct socket *so;
struct sockaddr_in6 lhost;
int hop_limit;
DEBUG_CALL("udp6_input");
DEBUG_ARG("m = %p", m);
if (slirp->restricted) {
goto bad;
}
ip = mtod(m, struct ip6 *);
m->m_len -= iphlen;
m->m_data += iphlen;
// (1)
uh = mtod(m, struct udphdr *);
m->m_len += iphlen;
m->m_data -= iphlen;
...
}
An attacker that is in control of the guest can issue a small IPv6 packet that will lead to the use of an udphdr
that has been initialized with invalid memory.
struct udphdr {
uint16_t uh_sport; /* source port */
uint16_t uh_dport; /* destination port */
int16_t uh_ulen; /* udp length */
uint16_t uh_sum; /* udp checksum */
};
If we use a debugger we can inspect the contents of uh
, at (1), and see that they are uninitialized (\xbe
is the pattern that AddressSanitizer uses to initialize allocations):
p/x *uh
(udphdr) $1 = (uh_sport = 0xbebe, uh_dport = 0xbebe, uh_ulen = 0xbebe, uh_sum = 0xbebe)
Impact
This issue may lead to out of bound read access
or indirect memory disclosure
.
Issue 3: Invalid pointer initialization in tftp.c:tftp_input (GHSL-2021-080)
The function tftp_input
handles requests for the tftp
protocol from the guest. While processing a udp
packet that is smaller than the size of the tftp_t
structure it uses memory from outside the working mbuf
buffer.
In the following code snippet the function mtod
is used without verifying the availability of enough bytes to properly cast the mbuf
into a tftp_t
type.
void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m)
{
// (1)
struct tftp_t *tp = (struct tftp_t *)m->m_data;
switch (ntohs(tp->tp_op)) {
case TFTP_RRQ:
tftp_handle_rrq(m->slirp, srcsas, tp, m->m_len);
break;
case TFTP_ACK:
tftp_handle_ack(m->slirp, srcsas, tp, m->m_len);
break;
case TFTP_ERROR:
tftp_handle_error(m->slirp, srcsas, tp, m->m_len);
break;
}
}
An attacker that is in control of the guest can issue a small packet that will lead to the use of an tftp_t
that has been initialized with invalid memory.
struct tftp_t {
struct udphdr udp;
uint16_t tp_op;
union {
struct {
uint16_t tp_block_nr;
uint8_t tp_buf[TFTP_BLOCKSIZE_MAX];
} tp_data;
struct {
uint16_t tp_error_code;
uint8_t tp_msg[TFTP_BLOCKSIZE_MAX];
} tp_error;
char tp_buf[TFTP_BLOCKSIZE_MAX + 2];
} x;
} SLIRP_PACKED;
If we use a debugger we can inspect the contents of tp
, at (1), and see that they are uninitialized (\xbe
is the pattern that AddressSanitizer uses to initialize allocations):
p/x *tp
(tftp_t) $0 = {
udp = (uh_sport = 0x0000, uh_dport = 0x4500, uh_ulen = 0x0000, uh_sum = 0x0000)
tp_op = 0xbebe
x = {
tp_data = (tp_block_nr = 0xbebe, tp_buf = "\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe ... \xbe\xbe"...)
tp_error = (tp_error_code = 0xbebe, tp_msg = "\xbe\xbe\xbe\xbe\xbe\xbe\xbe ... \xbe\xbe"...)
tp_buf = "\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe ... \xbe\xbe"...
}
}
Impact
This issue may lead to out of bound read access
or indirect memory disclosure
.
Issue 4: Invalid pointer initialization in udp.c:udp_input (GHSL-2021-081)
The function udp_input
handles requests for the udp
protocol from the guest. While processing a udp
packet that is smaller than the size of the udphdr
structure it uses memory from outside the working mbuf
buffer.
In the following code snippet the function mtod
is used without verifying the availability of enough bytes to properly cast the mbuf
into a udphdr
type.
void udp_input(register struct mbuf *m, int iphlen)
{
Slirp *slirp = m->slirp;
register struct ip *ip;
register struct udphdr *uh;
...
/*
* Get IP and UDP header together in first mbuf.
*/
ip = mtod(m, struct ip *);
uh = (struct udphdr *)((char *)ip + iphlen);
/*
* Make mbuf data length reflect UDP length.
* If not enough data to reflect UDP length, drop.
*/
len = ntohs((uint16_t)uh->uh_ulen);
...
}
An attacker that is in control of the guest can issue a small packet that will lead to the use of an udphdr
that has been initialized with invalid memory.
struct udphdr {
uint16_t uh_sport; /* source port */
uint16_t uh_dport; /* destination port */
int16_t uh_ulen; /* udp length */
uint16_t uh_sum; /* udp checksum */
};
If we use a debugger we can inspect the contents of uh
, at (1), and see that they are uninitialized (\xbe
is the pattern that AddressSanitizer uses to initialize allocations):
p/x *uh
(udphdr) $1 = (uh_sport = 0xbebe, uh_dport = 0xbebe, uh_ulen = 0xbebe, uh_sum = 0xbebe)
Impact
This issue may lead to out of bound read access
or indirect memory disclosure
.
CVE
- CVE-2021-3592 (bootp)
- CVE-2021-3593 (udp6)
- CVE-2021-3595 (tftp)
- CVE-2021-3594 (udp)
Resources
- https://gitlab.freedesktop.org/slirp/libslirp/-/blob/master/CHANGELOG.md#460-2021-06-14
- https://gitlab.freedesktop.org/slirp/libslirp/-/issues/44
- https://gitlab.freedesktop.org/slirp/libslirp/-/issues/45
- https://gitlab.freedesktop.org/slirp/libslirp/-/issues/46
- https://gitlab.freedesktop.org/slirp/libslirp/-/issues/47
Credit
This issue was discovered and reported by GHSL team member @agustingianni (Agustin Gianni).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to the corresponding GHSL-ID
’s in any communication regarding this issue.