10744046 2003-09-25 07:02 +0100 /122 rader/ Nick Cleaton <nick@cleaton.net>
Importerad: 2003-09-25 20:02 av Brevbäraren
Extern mottagare: bugtraq@securityfocus.com
Mottagare: Bugtraq (import) <29226>
Ärende: Cfengine2 cfservd remote stack overflow
------------------------------------------------------------
From: Nick Cleaton <nick@cleaton.net>
To: bugtraq@securityfocus.com
Message-ID: <20030925060223.GA332@lt1.cleaton.net>

==============================================================================

Background
==========

Cfengine (www.cfengine.org) automates the configuration and
maintenance of large computer networks.  A common setup involves
running the cfservd daemon on TCP port 5308 on a central master
server, with other hosts periodically connecting to the master to
check for configuration updates.

Vulnerability
=============

There is an exploitable stack overflow in the network I/O code used
in the cfservd daemon in Cfengine 2.x prior to version 2.0.8.
Arbitrary code execution has been demonstrated on x86 FreeBSD and is
believed to be possible on all platforms.

Cfengine 1 is not vulnerable, but downgrading is not recommended as
version 1 is nolonger supported by the author.

Cfengine 2 provides strong client authentication by redesigning a
stricter communications protocol. Responsibility for checking input
buffers is relocated in the new code and one important check was not
carried over.

The vulnerability occurs after an ACL check on the source IP of the
TCP connection, so this flaw can only be exploited from hosts that
are authorized to connect to the cfservd daemon, or from systems able
to spoof an authorized IP or trick an authorized host into forwarding
a connection.

The vulnerability can be exploited without reading any data from the server,
so blind spoofing may be feasible against some platforms.

The cfservd daemon is multithreaded rather than forking, so the
attacker only gets a single chance to get the offset correct within a
4096 byte window, less a few bytes for shellcode.  It may be possible
to increase this window by pre-populating other buffers before
triggering the overflow.

The vulnerable network I/O code is used in several other places in
Cfengine 2, so similar problems may exist in other pre-2.0.8 Cfengine
TCP servers and clients.

Vulnerable Versions
===================

Vulnerable:
   cfengine-2.0.0
   cfengine-2.0.1
   cfengine-2.0.2
   cfengine-2.0.3
   cfengine-2.0.4
   cfengine-2.0.5
   cfengine-2.0.5b1
   cfengine-2.0.5pre
   cfengine-2.0.5pre2
   cfengine-2.0.6
   cfengine-2.0.7
   cfengine-2.0.7p1
   cfengine-2.0.7p2
   cfengine-2.0.7p3
   cfengine-2.1.0a6
   cfengine-2.1.0a8
   cfengine-2.1.0a9

Not Vulnerable:
   cfengine-1.6.5 and earlier
   cfengine-2.0.8
   cfengine-2.0.8p1

Detection
=========

A failed attempt to exploit this vulnerability is likely to cause
cfservd to exit with a segfault, which will show up in the logs.

A successful attempt to exploit may show up as cfservd not running or
cfservd recently restarted, if the attacker is not particularly
sophisticated.

The log message "Bad transaction packet -- too long" indicates an
attempt to exploit a patched cfservd.

Fix
===

Upgrade to version 2.0.8p1 or later (recommended), or apply the
attached patch and rebuild cfengine.

The patch was made against 2.0.7p3, and may need to be adapted
slightly for some earlier versions of Cfengine 2.

Workaround
==========

Ensure that you have cfservd ACLs or firewall rules set up to allow
connections from trusted hosts only.

Details
=======

In BusyWithConnection() in cfservd.c, recvbuffer[] (a 4096 byte stack
buffer) is passed to ReceiveTransaction() in net.c.
ReceiveTransaction() then reads a message length as a six digit
decimal number from the TCP socket, and passes the buffer and the
length on to RecvSocketStream(), which attempts to read that many
bytes into the buffer.

If the length is greater than 4096, an overflow occurs and the return
address of BusyWithConnection() can be overwritten.

In tests on x86 FreeBSD, recvbuffer[] ends up within a few dozen
bytes of the top of the stack, so the attacker can only send a few
dozen extra bytes or cfservd will segfault before the attacker gets
control.

==============================================================================

--
Nick Cleaton
nick@cleaton.net
(10744046) /Nick Cleaton <nick@cleaton.net>/(Ombruten)
Bilaga (text/plain) i text 10744047
10744047 2003-09-25 07:02 +0100 /65 rader/ Nick Cleaton <nick@cleaton.net>
Importerad: 2003-09-25 20:02 av Brevbäraren
Extern mottagare: bugtraq@securityfocus.com
Mottagare: Bugtraq (import) <29227>
Bilaga (text/plain) till text 10744046
Ärende: Bilaga till: Cfengine2 cfservd remote stack overflow
------------------------------------------------------------
--- cfengine-2.0.7p3/src/net.c	Wed Apr 23 21:48:13 2003
+++ cfengine-2.0.8p1/src/net.c	Tue Sep  9 08:38:55 2003
@@ -89,7 +89,7 @@
 
 { char proto[9];
   char status;
-  unsigned int len;
+  unsigned int len = 0;
  
 bzero(proto,9);
 
@@ -101,6 +101,13 @@
 sscanf(proto,"%c %u",&status,&len);
 Debug("Transaction Receive [%s][%s]\n",proto,proto+8);
 
+if (len > bufsize - 8)
+   {
+   snprintf(OUTPUT,bufsize,"Bad transaction packet -- too long (%c %d) Proto = %s ",status,len,proto);
+   CfLog(cferror,OUTPUT,"");
+   return -1;
+   }
+
 if (strncmp(proto,"CAUTH",5) == 0)
    {
    Debug("Version 1 protocol connection attempted - no you don't!!\n");
@@ -132,6 +139,12 @@
 
 Debug("RecvSocketStream(%d)\n",toget);
 
+if (toget > bufsize)
+   {
+   CfLog(cferror,"Bad software request for overfull buffer","");
+   return -1;
+   }
+ 
 for (already = 0; already != toget; already += got)
    {
    got = recv(sd,buffer+already,toget-already,0);
@@ -144,7 +157,7 @@
  
    if (got == 0)   /* doesn't happen unless sock is closed */
       {
-      Debug("Transmission empty...\n");
+      Debug("Transmission empty or timed out...\n");
       fraction = 0;
       return already;
       }
@@ -178,6 +191,8 @@
 
 do
    {
+   Debug("Attempting to send %d bytes\n",tosend-already);
+
    sent=send(sd,buffer+already,tosend-already,flags);
    
    switch(sent)
@@ -191,6 +206,7 @@
 	  break;
       }
    }
+
 while(already < tosend);
 
 return already;
(10744047) /Nick Cleaton <nick@cleaton.net>/--------