Avoiding Server Hijacking

Avoiding Server Hijacking

Server hijacking happens when an application allows a local user to intercept and manipulate information meant for a server that the local user didn t start themselves. First let s get an idea of how such a thing could happen. When a server starts up, it first creates a socket and binds that socket according to the protocol you want to work with. If it s a Transmission Control Protocol (TCP) or User Datagram Protocol (UDP) socket, the socket is bound to a port. Less commonly used protocols might have very different addressing schemes. A port is represented by an unsigned short (16-bit) integer in C or C++, so it can range from 0 to 65535. The bind function looks like this:

int bind ( SOCKET s, const struct sockaddr FAR* name, int namelen );

This function is written to allow us to communicate using a wide variety of protocols. If you re writing code for Internet Protocol version 4 (IPv4), the variant you want to use is a sockaddr_in structure, which is defined like so:

struct sockaddr_in{ short sin_family; unsigned short sin_port; struct in_addr sin_addr; char sin_zero[8]; };

note

As of this writing, Internet Protocol version 6 (IPv6) is not in wide use, and I m going to confine my examples to IPv4. Unless otherwise noted, the concepts presented should be applicable to both protocols.

When you bind a socket, the important bits are the sin_port and sin_addr members. With a server, you d almost always specify a port to listen on, but the problem comes when we start dealing with the sin_addr member. The documentation on bind tells us that if you bind to INADDR_ANY (really 0), you re listening on all the available interfaces. If you bind to a specific IP address, you re listening for packets addressed to only that one address. Here s an interesting tweak in the way that sockets work that will bite you: it is possible to bind more than one socket to the same port.

The sockets libraries decide who wins and gets the incoming packet by determining which binding is most specific. A socket bound to INADDR_ANY loses to a socket bound to a specific IP address. One solution would be to identify and bind every available IP address on your server, but this is annoying. If you want to deal with the fact that network interfaces might be popping up (and going away) on the fly, you have to write a lot more code. Fortunately, you have a way out, which I ll illustrate in the following code example. A socket option named SO_EXCLUSIVEADDRUSE, which was first introduced in Microsoft Windows NT 4 Service Pack 4, solves this problem.

One of the reasons Microsoft introduced this socket option is the work of a fellow named Hobbit, who is associated with a very sharp hacker group called the L0pht. Hobbit wrote a great socket utility called Netcat, and he noticed that several servers under Windows NT had this binding problem. I ve written a demonstration application that shows off both the problem and the solution:

/* BindDemoSvr.cpp */ #include <winsock2.h> #include <stdio.h> #include <assert.h> #include "SocketHelper.h" //If you have an older version of winsock2.h #ifndef SO_EXCLUSIVEADDRUSE #define SO_EXCLUSIVEADDRUSE ((int)(~SO_REUSEADDR)) #endif /* This application demonstrates a generic UDP-based server. It listens on port 8391. If you have something running there, change the port number and remember to change the client too. */ int main(int argc, char* argv[]) { SOCKET sock; sockaddr_in sin; DWORD packets; bool hijack = false; bool nohijack = false; if(argc < 2 argc > 3) { printf("Usage is %s [address to bind]\n", argv[0]); printf("Options are:\n\t-hijack\n\t-nohijack\n"); return -1; } if(argc == 3) { //Check to see whether hijacking mode or no-hijack mode is //enabled. if(strcmp("-hijack", argv[2]) == 0) { hijack = true; } else if(strcmp("-nohijack", argv[2]) == 0) { nohijack = true; } else { printf("Unrecognized argument %s\n", argv[2]); return -1; } } if(!InitWinsock()) return -1; //Create your socket. sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(sock == INVALID_SOCKET) { printf("Cannot create socket - err = %d\n", GetLastError()); return -1; } //Now let's bind the socket. //First initialize the sockaddr_in. //I'm picking a somewhat random port that shouldn't have //anything running. if(!InitSockAddr(&sin, argv[1], 8391)) { printf("Can't initialize the sockaddr_in - doh!\n"); closesocket(sock); return -1; } //Let's demonstrate the hijacking and anti-hijacking options here. if(hijack) { BOOL val = TRUE; if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val)) == 0) { printf("SO_REUSEADDR enabled - Yo Ho Ho\n"); } else { printf("Cannot set SO_REUSEADDR - err = %d\n", GetLastError()); closesocket(sock); return -1; } } else if(nohijack) { BOOL val = TRUE; if(setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&val, sizeof(val)) == 0) { printf("SO_EXCLUSIVEADDRUSE enabled\n"); printf("No hijackers allowed!\n"); } else { printf("Cannot set SO_REUSEADDR - err = %d\n", GetLastError()); closesocket(sock); return -1; } } if(bind(sock, (sockaddr*)&sin, sizeof(sockaddr_in)) == 0) { printf("Socket bound to %s\n", argv[1]); } else { if(hijack) { printf("Curses! Our evil warez are foiled!\n"); } printf("Cannot bind socket - err = %d\n", GetLastError()); closesocket(sock); return -1; } //OK, now we've got a socket bound. Let's see whether someone //sends us any packets - put a limit so that we don't have to //write special shutdown code. for(packets = 0; packets < 10; packets++) { char buf[512]; sockaddr_in from; int fromlen = sizeof(sockaddr_in); //Remember that this function has a TRINARY return; //if it is greater than 0, we have some data; //if it is 0, there was a graceful shutdown //(shouldn't apply here); //if it is less than 0, there is an error. if(recvfrom(sock, buf, 512, 0, (sockaddr*)&from, &fromlen) > 0) { printf("Message from %s at port %d:\n%s\n", inet_ntoa(from.sin_addr), ntohs(from.sin_port), buf); //If we're hijacking them, change the message and //send it to the real server. if(hijack) { sockaddr_in local; if(InitSockAddr(&local, "127.0.0.1", 8391)) { buf[sizeof(buf)-1] = '\0'; strncpy(buf, "You are hacked!", sizeof(buf) -1); if(sendto(sock, buf, strlen(buf) + 1, 0, (sockaddr*)&local, sizeof(sockaddr_in)) < 1) { printf("Cannot send message to localhost - err = %d\n", GetLastError()); } } } } else { //I'm not sure how we get here, but if we do, //we'll die gracefully. printf("Ghastly error %d\n", GetLastError()); break; } } return 0; }

This sample code is also available on the companion CD in the folder Secureco\Chapter 9\BindDemo. Let s quickly review how the code works, and then we ll look at some results. I ve hidden a couple of helper functions in SocketHelper.cpp I ll be reusing these functions throughout the chapter. I also hope that the code might turn out to be useful in your own applications.

First we check the arguments. I have two options available: hijack and nohijack. We ll use the hijack option on the attacker and the nohijack option to prevent the attack. The difference here is which socket options we set. The hijack option uses SO_REUSEADDR to allow the attacker to bind to an active port. The nohijack option uses SO_EXCLUSIVEADDRUSE, which prevents SO_REUSEADDR from functioning. If you specify no options, the server will just bind the port normally. Once the socket is bound, we ll then log where the packet originated from and the message. If we re attacking the other server, we ll change the message to show the consequences of this problem.

So, let s take a look at what happens if the server doesn t use SO_EXCLUSIVEADDRUSE. Invoke the victim server with this:

BindDemo.exe 0.0.0.0

Next invoke the attacker with the following substitute 192.168.0.1 with your own IP address:

BindDemo.exe 192.168.0.1 -hijack

Now use the client to send a message:

BindDemoClient.exe 192.168.0.1

Here are the results from the attacker:

SO_REUSEADDR enabled - Yo Ho Ho Socket bound to 192.168.0.1 Message from 192.168.0.1 at port 4081: Hey you!

Here s what the victim sees:

Socket bound to 0.0.0.0 Message from 192.168.0.1 at port 8391: You are hacked!

If your application uses careful logging, you might notice that this attacker was a little sloppy and left some traces. Any logs you might have show packets originating from the server itself. Do not let this give you any comfort when we get into spoofing later in this chapter, I ll show you how this could have been trivially overcome by the attacker.

Now, here s how to do it right. Invoke the server no longer a hapless victim with

BindDemo.exe 0.0.0.0 nohijack

Start the attacker as before with

BindDemo.exe 192.168.0.1 hijack

The server responds with

SO_EXCLUSIVEADDRUSE enabled - no hijackers allowed! Socket bound to 0.0.0.0

And the attacker complains:

SO_REUSEADDR enabled - Yo Ho Ho Curses! Our evil warez are foiled! Cannot bind socket - err = 10013

Now, when the client sends a message, our server gets the right one:

Message from 192.168.0.1 at port 4097: Hey you!



Writing Secure Code
Writing Secure Code, Second Edition
ISBN: 0735617228
EAN: 2147483647
Year: 2005
Pages: 153

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net