Tuesday, August 01, 2006

Using divert sockets on Mac OS X

A note of caution: using the ip firewall, especially at the programming level makes it very easy for you to lock yourself out from the box. I cannot guarantee that you won't loose any data because you've had to reset your computer to regain access so please be careful.

About divert sockets


One of Tiger's heritages from the BSD world is the ip firewall extensions and APIs. ipfw allows you to use divert sockets. A divert socket is a network socket, that once bound to a specified port will receive all packets diverted to that port.

This means you can let the kernel know that you want it to divert all incoming HTTP TCP traffic to port 8787:

00001 divert 8787 tcp from any 80 to 193.231.199.23 in

once ipfw has loaded this rule, a divert socket bound on port 8787 will be able to read all incoming TCP packets.

The beauty of it is that you can re-inject the packet on the network, even after you've altered it.

Our sample program


Our demonstration program will look for the string "LoudHush" in the data part of all the packets we've received and will replace it with a more hax0rish variant: "l0UDhUSH".
This example has a number of bad assumptions such as altering text both inside the page's content and within anchors, using a very slow search algorithm, etc.

But it's simple, and simple is what we're after.

So let's take a look at it:

Note the intHandler function; we use it to delete the rule once the user presses Ctrl+C to stop our program.
If you don't use the intHandler you'd have to delete the rule using the ipfw commands.


/* remove the firewall rule when exit */
void intHandler (int signo)
{

if(setsockopt(fw_sock, IPPROTO_IP, IP_FW_DEL, &fw, sizeof(fw))==-1)
{
fprintf(stderr, "%s: could not remove rule: %s\n", progname, strerror(errno));
exit(2);
}

close(fw_sock);
exit(0);
}


We open the divert socket:


/* open a divert socket */
fd=socket(AF_INET, SOCK_RAW, IPPROTO_DIVERT);
[..]
ret=bind(fd, (struct sockaddr *)&bindPort, sizeof(bindPort));

We now have to let the ipfw know that we want it to divert packets to our listening port (as bound by the divert socket):

bzero(&fw, sizeof (struct ip_fw));
fw.version = IP_FW_CURRENT_API_VERSION;
fw.fw_number = 1;
fw.fw_dst.s_addr = *(in_addr_t*)(temp_hostent->h_addr);
fw.fw_dmsk.s_addr = ~0; //any source
fw.fw_prot = IPPROTO_TCP; //tcp
fw.fw_flg = IP_FW_F_DIVERT | IP_FW_F_IN; //divert and incoming
fw.fw_un.fu_divert_port=htons(bindPort.sin_port);
fw.fw_uar.fw_pts[0] = 80; //remote port - HTTP
fw.fw_nports = 1;


We finally open the socket and add our divert rule to the firewall chain:

if ((fw_sock=socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==-1)
[..]
if (setsockopt(fw_sock, IPPROTO_IP, IP_FW_ADD, &fw, sizeof(fw))==-1)
/* install signal handler to delete the rule */
signal(SIGINT, intHandler);


We then block to read any packets we receive on our divert port and search and replace a string in the TCP payload:

n=recvfrom(fd, packet, BUFSIZE, 0, (struct sockaddr *)&sin, (socklen_t *)&sinlen);
[..]
memreplace(data, "LoudHush", n, strlen("LoudHush"), "l0UDhUSH");
[..]
n=sendto(fd, packet, n ,0, (struct sockaddr *)&sin, sinlen);


Sow what do we get when running the sample code?














loudhush.ro without the divert filter

loudhush.ro with the divert filter

Before

After


Useful divert scenarios


So can we build anything useful using divert sockets?
Well, one could encrypt IAX traffic on the fly and decrypt it on the remote side.

I have no idea if this is how Zfone works, but I think divert sockets would make a viable implemention option for OS X.

Getting the sample code



Download the sample file here.
Compile using:

gcc -g -DREINJECT -DFIREWALL -c divert.m -o divert.o
gcc -o divert divert.o -framework Cocoa -framework SystemConfiguration



Leopard / 10.5 update



Starting with Leopard, you also need to re-compute the TCP checksum of the altered packet, otherwise the kernel will drop the packet as it refuses to re-compute the checksum for you:


memreplace(data, "LoudHush", n, strlen("LoudHush"), "l0UDhUSH");

//zero the tcp checksum before check-summing the packet otherwise the checksum is wrong
tcpptr->th_sum = 0;
unsigned short cs= computeTCPChecksum(hdr, tcpptr);
//printf("Computed checksum: %d\n", cs);
tcpptr->th_sum = cs;

2 comments :

felix groebert said...

great stuff.

i used ip_queue on linux to do simliar stuff - nice to see how i am able to use old bsd features on my osx.

Anonymous said...

Interesting applications of divert socket programming (should be easy to port to OSX):
https://github.com/echothrust/pf-diverters