DNS Query poisoning Application UDP socket descriptor

Bug #1448396 reported by Vipin Gahlaut
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
eglibc (Ubuntu)
New
Undecided
Unassigned

Bug Description

Problem Description:

When DNS query is fired from ubuntu (14.04) it uses random source port to send outgoing DNS request. It has been observed that random source port is not being checked if the port is already in use by other user application. As a result When DNS server sends the response, response is received by user application which may cause corruption/security issues in the application code.

To make the problem easily reproducible we have increased few system limits to create 25000 UDP servers so that UDP port conflict with DNS happen frequntly

Steps to Reproduce problem:
========================
Step 1. Increase limit

    Per-User Limit

    Open file: /etc/security/limits.conf

    Paste following towards end:

    * hard nofile 500000
    * soft nofile 500000
    root hard nofile 500000
    root soft nofile 500000

    System-Wide Limit

    Set this higher than user-limit set above.

    Open /etc/sysctl.conf

    Add following:

    fs.file-max = 2097152

Step 2. Reboot ubuntu

Step 3. Verify new limits

    Use following command to see max limit of file descriptors:
    #cat /proc/sys/fs/file-max

    Hard Limit
    #ulimit -Hn

    Soft Limit
    #ulimit -Sn

Step 4. Put following code in server.c and compile it (gcc server.c -o server). This will bind 25K UDP ports.

/*
 * server.c
 *
 * Author: Vipin Kumar Gahlaut
 *
 * CoreEmbedded Technologies Pvt Ltd
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>

#define MAX_CONNECTIONS 25000
#define MAX_IP_STR 16
#define MAX_EPOLL_EVENTS_PER_RUN 1

int setup_udp_server(char *ip, int port)
{
    int sock;
    struct sockaddr_in saddr;

    saddr.sin_family = AF_INET;
    saddr.sin_port = htons (port);
    saddr.sin_addr.s_addr = inet_addr(ip);

    if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
    {
       printf("Failed to created UDP Socket :%m\n");
        return -1;
    }

    if (bind (sock, (struct sockaddr *)&saddr, sizeof (saddr)) < 0)
    {
        close (sock);
        printf("Failed to bind on UDP port :%m\n");
        return -1;
    }

    return sock;
}

int main (int argc, char *argv[])
{
    char ip[MAX_IP_STR];
    char buf[1024];
    int efd,sfd,nfds;
    struct epoll_event ev,*events;
    int i=0,len;
    struct sockaddr_in saddr;
    socklen_t addrlen=sizeof(struct sockaddr_in);

    if(argc < 2)
    {
        printf("Usage: server <ip>\n");
        exit(0);
    }
    if(strlen(argv[1]) >= MAX_IP_STR)
    {
        printf("Failed: Invalid IP Address\n");
        exit(0);
    }
    strcpy(ip,argv[1]);

    if((efd = epoll_create1(0)) < 0)
    {
        printf("Failed: epoll_create1: %m\n");
        exit(0);
    }

    for(i=0; i<MAX_CONNECTIONS; i++)
    {
        if((sfd=setup_udp_server(ip, 0)) < 0)
        {
            exit(0);
        }
        ev.events = EPOLLIN | EPOLLET | EPOLLERR;
        ev.data.fd = sfd;

        if (epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &ev) < 0)
        {
            printf("Couldn't add client socket to epoll set: %m\n");
            exit(0);
        }
        if (getsockname(sfd, (struct sockaddr *)&saddr, &addrlen ))
        {
            printf("Failed to get port for socket: %m\n");
            exit(0);
        }
        printf("FD: %d Port: %d Number of active servers: %d\n",sfd, ntohs(saddr.sin_port), i);
    }

    events = (struct epoll_event *)malloc(
                    MAX_EPOLL_EVENTS_PER_RUN * sizeof(struct epoll_event));

    if(events == NULL)
    {
        exit(0);
    }

    while(1)
    {
        nfds = epoll_wait(efd, events, MAX_EPOLL_EVENTS_PER_RUN, -1);

        if (nfds < 0)
        {
            printf("epoll_wait failed: %m\n");
            exit(0);
        }

        for(i = 0; i < nfds; i++)
        {
            if (getsockname(events[i].data.fd, (struct sockaddr *)&saddr, &addrlen ))
            {
                printf("Failed to get port for socket: %m\n");
                exit(0);
            }

            printf("epoll waked on fd: %d port %d\n",events[i].data.fd,ntohs(saddr.sin_port));
            if(events[i].events & EPOLLIN)
            {
                len = recvfrom(events[i].data.fd, buf, 1024, 0,
                        (struct sockaddr *)&saddr, &addrlen);

                printf("Received %d bytes on %d fd %m\n",len, events[i].data.fd);
                printf("Source IP:Port %s:%d\n",inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
            }
        }

    }

    close(efd);
    return 0;
}

Step 5. Run server program by providing IP address of machine (example ./server 192.168.0.2)

Step 6. User following shell script in another terminal to initiate DNS queries

#!/bin/bash
COUNTER=0
    while [ $COUNTER -lt 100 ]; do
    nslookup www.ubuntu.com
done

Step 7. Observe that sometime DNS response is received by ./server instead of DNS client

Problem Analysis: We have observed on wireshark that when problem happens source port used by DNS client/resolver library etc is one of the source port on which user application (in this case server) is already bind on that port and using it.

This is a big concern because it causes problem both in DNS client (times out) and user application received spurious data. To my opinion this is a security issue as I am receiving data on a port that I have not exposed to anybody and depending on data it my crash/hijack my application. So I am also marking this bug as a security vulnerability so that security team can better asses the situation.

Please note that With small number of UDP server chances of port conflict reduces but does not eliminate problem. DNS clinet/resolver library must have checked if source port is already in use or better to bind on port 0 (zero) so that system allocates available port not just random port. DNS client need to use random **available** port not just a random port.

ProblemType: Bug
DistroRelease: Ubuntu 14.04
Package: libc6-dev 2.19-0ubuntu6
ProcVersionSignature: Ubuntu 3.13.0-24.46-generic 3.13.9
Uname: Linux 3.13.0-24-generic i686
ApportVersion: 2.14.1-0ubuntu3
Architecture: i386
CurrentDesktop: Unity
Date: Sat Apr 25 13:03:30 2015
InstallationDate: Installed on 2014-05-20 (339 days ago)
InstallationMedia: Ubuntu 14.04 LTS "Trusty Tahr" - Release i386 (20140417)
SourcePackage: eglibc
UpgradeStatus: No upgrade log present (probably fresh install)

Revision history for this message
Vipin Gahlaut (gailu96) wrote :
Vipin Gahlaut (gailu96)
summary: - DNS Query causes corruption on other UDP sockets used by user
- application due to source port reuse
+ DNS Query poisoning Application UDP socket descriptor
Vipin Gahlaut (gailu96)
information type: Private Security → Public
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.