FOLLOWUP: Re: SECURITY: Sockets follow symbolic links

From: Sylvain Robitaille <syl_at_alcor.concordia.ca>
Date: Thu, 30 Sep 1999 12:13:05 -0400 (EDT)

On Wed, 29 Sep 1999, I wrote:

> The problem isn't really in ssh, but rather in the fact that some OSes
> (including DEC Unix 4.0e -- I haven't tested other versions) will follow
> a symbolic link when creating sockets ...


Mike Iglesias replied

> DU 4.0B, 4.0D, and Tru64 Unix 4.0F are all vulnerable to this DoS, according
> to the test program. DU 4.0C is probably also vulnerable since it's
> 4.0B with additions for new hardware. So, it looks like at least 4.0B-F.
>
> I haven't tried 5.0 yet.


Continuing with my original message:

> I submit the following patch for anyone who is using ssh, (created with
> the ssh-1.2.27 sources, but it should apply cleanly to most recent
> versions), in order to protect your systems.


Ann Cantelow sent the following:

> ... the compile failed for me, missing snprintf. I applied your patch
> to ssh version 1.2.23, so I'm a bit behind. I checked without the
> patch; that compiled fine. I checked with cc instead of gcc; that
> was missing both snprintf and __eprintf.

I checked the Changelog file included with Ssh-1.2.27 and it looks as
though they only recently added the snprintf code. I strongly recommend
that anyone wanting to use this patch upgrade to the current ssh at the
same time.


Ann also reminded me:

> DU native patch didn't work on it ...

She's right of course. I use GNU patch on all my systems, so unified
context diffs work for me. I apologize to those who tried to apply my
patch only to have patch report that it can't find a patch...


Furthermore, one of the folks discussing this issue on Bugtraq found a
zero-offset error in my patch. :-(

I append an updated patch (a regular context diff this time) that fixes
that error. Hopefully there won't be any others.

People should note, of course that a race condition still exists even
after this patch is applied. The place to really fix the problem is in
the kernel code, but I'm not equipped for that. However this will at
least make it more difficult for users to exploit.

-- 
----------------------------------------------------------------------
Sylvain Robitaille                              syl_at_alcor.concordia.ca
 
Systems Manager                                   Concordia University
Instructional & Information Technology        Montreal, Quebec, Canada
----------------------------------------------------------------------
*** newchannels.c.original	Wed May 12 07:19:27 1999
--- newchannels.c	Thu Sep 30 11:50:11 1999
***************
*** 16,21 ****
--- 16,26 ----
  */
  
  /*
+  * Revision 1.50unoff 1999/09/29 14:27:52  <syl_at_alcor.concordia.ca>
+  *      ssh-agent symlink DoS exploit protection added
+  */
+ 
+ /*
   * $Id: newchannels.c,v 1.49 1999/02/22 08:14:01 tri Exp $
   * $Log: newchannels.c,v $
   * Revision 1.49  1999/02/22 08:14:01  tri
***************
*** 317,323 ****
  
  #define STATUS_TERMINATE                0x003f
  
! /* Data structure for channel data.  This is iniailized in channel_allocate
     and cleared in channel_free. */
  
  typedef struct
--- 322,398 ----
  
  #define STATUS_TERMINATE                0x003f
  
! /*
!  * 1999/09/29 Sylvain Robitaille: Used for mailing to the sysadmin(s) if
!  *            we catch someone trying the exploit to cause the agent
!  *            socket to be created at the other end of a sym-link.
!  */
! #define MAIL_RECIPIENT "sysadm"
! #define MAIL_COMMAND   "/usr/bin/mailx -s 'URGENT: POSSIBLE SSH-AGENT SYMLINK ATTACK' " MAIL_RECIPIENT
! 
! /*
!  *  Mail a complaint to the sysadmin.
!  */
! void mail_report(char *dirname, char *linkname, struct stat *st)
! {
!    FILE *mail_pipe;
!    struct passwd *pw;
!    struct group  *gr;
!    char usrname[17];
!    char grpname[17];
!    char target[PATH_MAX];
!    char filename[PATH_MAX];
!    int  count;
! 
!    if ((mail_pipe = popen(MAIL_COMMAND, "w")) == NULL)
!    {
!       snprintf(target, sizeof(target) - 1, "can't open pipe to %s\n",
!                MAIL_COMMAND);
!       log_msg(target);
!       exit(1);
!    }
! 
!    /* reconstruct the full path to the file */
!    if (dirname[strlen(dirname)-1] == '/') dirname[strlen(dirname)-1] = 0;
!    snprintf(filename, sizeof(filename) - 1, "%s/%s", dirname, linkname);
! 
!    /* Figure out who owns the file. */
!    pw = getpwuid(st->st_uid);
!    if (!pw) {
!       snprintf(usrname, sizeof(usrname) - 1, "%d", st->st_uid);
!    } else {
!       snprintf(usrname, sizeof(usrname) - 1, "%s", pw->pw_name);
!    }
!    
!    gr = getgrgid(st->st_gid);
!    if (!gr) {
!       snprintf(grpname, sizeof(grpname) - 1, "%d", st->st_gid);
!    } else {
!       snprintf(grpname, sizeof(grpname) - 1, "%s", gr->gr_name);
!    }
!    
!    /*
!     * Get the link's target. NOTE: The target may not necessarily
!     * already exist, so we don't bother getting stats on it, but we
!     * do want to know its path.
!     */
!    count = readlink(filename, target, sizeof(target) - 1);
!    if (count < 0) {
!       snprintf(target, sizeof(target) - 1, "unreadable!");
!    } else {
!       target[count] = '\0';
!    }
! 
!    (void) fprintf(mail_pipe, "File: %s exists as a symbolic link\n", filename);
!    (void) fprintf(mail_pipe, "Owner: %s.%s\n", usrname, grpname);
!    (void) fprintf(mail_pipe, "Target: %s\n", target);
!    (void) fprintf(mail_pipe, "Mode: %o\n", 07777 & st->st_mode);
!    (void)  fflush(mail_pipe);
! 
!    (void) pclose(mail_pipe);
! }
! 
! /* Data structure for channel data.  This is initialized in channel_allocate
     and cleared in channel_free. */
  
  typedef struct
***************
*** 2395,2400 ****
--- 2470,2502 ----
        return 0;
      }
    
+   /*
+    * 1999/09/28 Sylvain Robitaille: Check that there isn't already a
+    *            file by the same name. If there is, and it's a symbolic
+    *            link, we probably have a DoS attempt. Else, if the file
+    *            exists and is not already a socket, complain and fail.
+    *
+    *            File exists but is a socket is acceptable, since that
+    *            socket might be a remnant (did the machine crash while
+    *            that socket was still open?)
+    */
+   ret = lstat(channel_forwarded_auth_socket_name, &st);
+   if (!ret && S_ISLNK(st.st_mode)) {
+     packet_send_debug("* Remote error: Agent socket creation failed: File exists as a symbolic link.");
+     packet_send_debug("* Remote error: Authentication fowarding disabled.");
+     log_msg("Agent socket creation failed: File %s is a symbolic link.",
+             channel_forwarded_auth_socket_name);
+     mail_report(channel_forwarded_auth_socket_dir_name,
+                 channel_forwarded_auth_socket_name, &st);
+     return 0;
+   } else if (!ret && !S_ISSOCK(st.st_mode)) {
+     packet_send_debug("* Remote error: Agent socket creation failed: File exists and is not a socket.");
+     packet_send_debug("* Remote error: Authentication fowarding disabled.");
+     log_msg("Agent socket creation failed: File %s is not a socket.",
+             channel_forwarded_auth_socket_name);
+     return 0;
+   }
+ 
    /* Create the socket. */
    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0)
Received on Thu Sep 30 1999 - 17:36:45 NZST

This archive was generated by hypermail 2.4.0 : Wed Nov 08 2023 - 11:53:39 NZDT