| Summary: | Serious multiple Linux <= 2.4.28 and 2.6.9 IGMP vulnerabilities (Vendor-Sec) | ||
|---|---|---|---|
| Product: | Gentoo Security | Reporter: | Sune Kloppenborg Jeppesen (RETIRED) <jaervosz> |
| Component: | Kernel | Assignee: | Gentoo Security <security> |
| Status: | RESOLVED DUPLICATE | ||
| Severity: | normal | ||
| Priority: | High | ||
| Version: | unspecified | ||
| Hardware: | All | ||
| OS: | All | ||
| Whiteboard: | CLASSIFIED / 20041214? | ||
| Package list: | Runtime testing required: | --- | |
|
Description
Sune Kloppenborg Jeppesen (RETIRED)
2004-12-03 04:07:41 UTC
Just reported to Vendor-Sec, no patches, PoC provided but not included here.
There are multiple serious issues with the igmp.c code in recent kernel
versions. Here an incomplete list of bugs:
1) ip_mc_source suffers from a serious kernel deadlock & kernel memory
overwrite problem (see attached code). It is possible to decrement the
psl->sl_count counter to be 0xffffffff == -1 with the consequence that:
- a repeated call will start a loop counting from 0 to UINT_MAX causing a
kernel hang for minutes (depending on the machine speed), then the
whole kernel memory following the kmalloc'ated buffer will be shifted by 4
bytes (LOL) causing an immediate reboot.
- If properly exploited this will lead to elevated privileges.
Btw. the code there is obviously nonsense:
rv = !0;
for (i=0; i<psl->sl_count; i++) {
rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
sizeof(__u32));
if (rv >= 0)
break;
}
if (!rv) /* source not found */
goto done;
If we had the 01.04 today I would say some people have keyboards without
the '[' and ']' brackets ;-)
2) because of the bug 1) it is possible to read huge portions of kernel
memory through ip_mc_msfget()
3) igmp_marksources() function is called in the context of an IGMP group
query and suffers from an out of bound access to kernel memory. This is
remotely exploitable if applications have bound a multicast socket.
I'm not sure but I guess that there is a large number of standard apps
that use multicast, eg. some Linux routing packages or videoconferencing,
etc.
You may check for your configuration if you are vulnerable:
/proc/net/igmp
/proc/net/mcfilter
if both files exist and are non-empty you are vulnerable.
This bug allows in the best case to remotely DoS a Linux machine with just
a moderate flow of prepared IGMP packets.
In the worst case (the socket buffer allocated for the packet is at the
end of kernel accessible memory), the Linux machine may be crashed
remotely.
4) I think there are more bugs and more or less subtle races in that code.
And seriously, please save me from the 'I have a patch for X' 'I have a
patch for Y' mentality, I feel like that code or at least parts of it have
been done by people _without_ any elementary C and kernel API & principles
knowledge. It _must_ be completely reaudited. I really wonder, since I
remember that it was mostly clean in .22 or .23 or so, so who the hell is
breaking it again and again and why??
Proposed fix from Chris Wright: ===== net/ipv4/igmp.c 1.58 vs edited ===== --- 1.58/net/ipv4/igmp.c Proposed fix from Chris Wright:
===== net/ipv4/igmp.c 1.58 vs edited =====
--- 1.58/net/ipv4/igmp.c 2004-11-09 16:44:25 -08:00
+++ edited/net/ipv4/igmp.c 2004-12-04 01:14:53 -08:00
@@ -1774,16 +1774,16 @@ int ip_mc_source(int add, int omode, str
psl = pmc->sflist;
if (!add) {
- if (!psl)
+ if (!psl || !psl->sl_count)
goto done;
- rv = !0;
+ rv = 1;
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
+ rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
sizeof(__u32));
- if (rv >= 0)
+ if (rv >= 0) /* array is sorted */
break;
}
- if (!rv) /* source not found */
+ if (rv) /* source not found */
goto done;
/* update the interface filter */
@@ -1825,7 +1825,7 @@ int ip_mc_source(int add, int omode, str
}
rv = 1; /* > 0 for insert logic below if sl_count is 0 */
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
+ rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
sizeof(__u32));
if (rv >= 0)
break;
===== net/ipv6/mcast.c 1.71 vs edited =====
--- 1.71/net/ipv6/mcast.c 2004-11-11 15:07:25 -08:00
+++ edited/net/ipv6/mcast.c 2004-12-04 01:27:20 -08:00
@@ -387,16 +387,16 @@ int ip6_mc_source(int add, int omode, st
psl = pmc->sflist;
if (!add) {
- if (!psl)
+ if (!psl || !psl->sl_count)
goto done;
- rv = !0;
+ rv = 1;
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, group,
+ rv = memcmp(&psl->sl_addr[i], source,
sizeof(struct in6_addr));
- if (rv >= 0)
+ if (rv >= 0) /* array is sorted */
break;
}
- if (!rv) /* source not found */
+ if (rv) /* source not found */
goto done;
/* update the interface filter */
@@ -437,7 +437,7 @@ int ip6_mc_source(int add, int omode, st
}
rv = 1; /* > 0 for insert logic below if sl_count is 0 */
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, group, sizeof(struct in6_addr));
+ rv = memcmp(&psl->sl_addr[i], source, sizeof(struct in6_addr));
if (rv >= 0)
break;
}
Minor revision to the last patch. Minor revision to the last patch. Eliminates some no-op changes.
===== net/ipv4/igmp.c 1.58 vs edited =====
--- 1.58/net/ipv4/igmp.c 2004-11-09 16:44:25 -08:00
+++ edited/net/ipv4/igmp.c 2004-12-06 18:43:54 -08:00
@@ -1778,12 +1778,12 @@ int ip_mc_source(int add, int omode, str
goto done;
rv = !0;
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
+ rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
sizeof(__u32));
- if (rv >= 0)
+ if (rv >= 0) /* array is sorted */
break;
}
- if (!rv) /* source not found */
+ if (rv) /* source not found */
goto done;
/* update the interface filter */
@@ -1825,7 +1825,7 @@ int ip_mc_source(int add, int omode, str
}
rv = 1; /* > 0 for insert logic below if sl_count is 0 */
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
+ rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
sizeof(__u32));
if (rv >= 0)
break;
===== net/ipv6/mcast.c 1.71 vs edited =====
--- 1.71/net/ipv6/mcast.c 2004-11-11 15:07:25 -08:00
+++ edited/net/ipv6/mcast.c 2004-12-06 18:43:54 -08:00
@@ -391,12 +391,12 @@ int ip6_mc_source(int add, int omode, st
goto done;
rv = !0;
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, group,
+ rv = memcmp(&psl->sl_addr[i], source,
sizeof(struct in6_addr));
- if (rv >= 0)
+ if (rv >= 0) /* array is sorted */
break;
}
- if (!rv) /* source not found */
+ if (rv) /* source not found */
goto done;
/* update the interface filter */
@@ -437,7 +437,7 @@ int ip6_mc_source(int add, int omode, st
}
rv = 1; /* > 0 for insert logic below if sl_count is 0 */
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, group, sizeof(struct in6_addr));
+ rv = memcmp(&psl->sl_addr[i], source, sizeof(struct in6_addr));
if (rv >= 0)
break;
}
New version: ===== net/ipv4/igmp.c 1.58 vs edited ===== --- 1.58/net/ipv4/igmp.c New version:
===== net/ipv4/igmp.c 1.58 vs edited =====
--- 1.58/net/ipv4/igmp.c 2004-11-09 16:44:25 -08:00
+++ edited/net/ipv4/igmp.c 2004-12-10 15:16:17 -08:00
@@ -1778,12 +1778,12 @@ int ip_mc_source(int add, int omode, str
goto done;
rv = !0;
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
+ rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
sizeof(__u32));
- if (rv >= 0)
+ if (rv == 0)
break;
}
- if (!rv) /* source not found */
+ if (rv) /* source not found */
goto done;
/* update the interface filter */
@@ -1825,9 +1825,9 @@ int ip_mc_source(int add, int omode, str
}
rv = 1; /* > 0 for insert logic below if sl_count is 0 */
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
+ rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr,
sizeof(__u32));
- if (rv >= 0)
+ if (rv == 0)
break;
}
if (rv == 0) /* address already there is an error */
===== net/ipv6/mcast.c 1.71 vs edited =====
--- 1.71/net/ipv6/mcast.c 2004-11-11 15:07:25 -08:00
+++ edited/net/ipv6/mcast.c 2004-12-10 17:20:46 -08:00
@@ -391,12 +391,12 @@ int ip6_mc_source(int add, int omode, st
goto done;
rv = !0;
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, group,
+ rv = memcmp(&psl->sl_addr[i], source,
sizeof(struct in6_addr));
- if (rv >= 0)
+ if (rv == 0)
break;
}
- if (!rv) /* source not found */
+ if (rv) /* source not found */
goto done;
/* update the interface filter */
@@ -437,8 +437,8 @@ int ip6_mc_source(int add, int omode, st
}
rv = 1; /* > 0 for insert logic below if sl_count is 0 */
for (i=0; i<psl->sl_count; i++) {
- rv = memcmp(&psl->sl_addr, group, sizeof(struct in6_addr));
- if (rv >= 0)
+ rv = memcmp(&psl->sl_addr[i], source, sizeof(struct in6_addr));
+ if (rv == 0)
break;
}
if (rv == 0) /* address already there is an error */
IGMP has been assigned CAN-2004-1137 BK changesets : http://linux.bkbits.net:8080/linux-2.4/cset@41b76e94BsJKm8jhVtyDat9ZM1dXXg http://linux.bkbits.net:8080/linux-2.6/cset@41b768d1ySHbfa7cUWDle8NjDT_02A http://linux.bkbits.net:8080/linux-2.6/cset@41b76c07Ee61GkoNwMH-oOvWG2FdxA Ok, is this cleared for declassification since it's fixed upstream then...? This bug is and will stay classified (quoting emails sent to v-s and requesting secret). Another one will be opened to handle the issue when it will be public. This is however semi-public since the fixes are out there, so we can start including patches in our trees too. Any publicity about it should however wait for advisory publication (due Dec 14 20:00 UTC). *** Bug 73802 has been marked as a duplicate of this bug. *** |