HOWTO: bind to a non local address (transparent proxy)

In certain situations, you may want to send packets as if they're coming from a different computer. Linux prevents such IP address spoofing by default, because the most well known use is as a malicious spoofing attack. Still, there are legitimate reasons. For instance, a transparent proxy intercepts traffic and replies in name of the original destination. Especially with larger sites, it is common to setup a virtual destination address and have a set of servers handle the load by mimicking this virtual host.

In Linux 2.6+, to spoof packets in IPv4, bind an INET socket to a non-local address, as in this straightforward example:

 

#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define SPOOFPORT  80                // really, whichever you want
#define SPOOFADDR  ((214 << 24) + 1) // address to impersonate

int main(int argc, char **argv)
{
  struct sockaddr_in spoofaddr;
  int fd;

  fd = socket(PF_INET, SOCK_DGRAM, 0);
  if (fd == -1) {
    perror("socket");
    return 1;
  }

  memset(&spoofaddr, 0, sizeof(spoofaddr));
  spoofaddr.sin_family      = AF_INET;
  spoofaddr.sin_port        = htons(SPOOFPORT);
  spoofaddr.sin_addr.s_addr = htonl(SPOOFADDR);
  if (bind(fd, (void*) &spoofaddr, sizeof(spoofaddr))) {
    perror("bind");
    return 1;
  }

  if (close(fd)) {
    perror("close");
    return 1;
  }
  printf("OK\n");
  return 0;
}

When executed on most platforms, this piece of code will print
bind: Cannot assign requested address
To enable transparent proxy support in bind, two prerequisites must be met: (1) the application must have the CAP_NET_ADMIN capability and (2) the host must allow transparent proxying. The easiest way to enable the first is to start with superuser privileges. Obviously, drop these privileges as soon as they are no longer needed. The second setting is configured through a procsfs. Make sure that /proc/sys/net/ipv4/ip_nonlocal_bind is set to 1.

Now, the code should execute succesfully and you can send to any host while masquerading as coming from the Department of Defense. Yes, that's the owner of 214.x.x.x. May want to change that macro.

Comments