I have a web app written in c++ using TCP/IP over standard network sockets, running on Linux. The service is open to the wild and woolly internet.
Periodically I get bursts of abusive requests from spammers running automated scripts. I can detect these and close down the socket. Right now I just do a polite socket close like I would do to any valid request that has completed, with a socket lib close like this:
close( mSocket );
But sometimes closing the socket normally notifies the spam script that the socket connection has terminated, and they immediately initiate another fraudulent request.
What is the best way to terminate a TCP/IP connection that cleans up the open socket on my system, but leaves the remote party hanging. That is I want to close a socket in the way that is lowest cost for me, but highest cost for them.
@Nicholas Wilson:
Using TCP_REPAIR seems like a good idea. When a socket is closed in TCP_REPAIR mode no FIN or RST packet is sent. The remote socket is left hanging. I'm going to try it and report back. Here is my (untested) code:
if ( abuse )
{
int aux = 1;
if ( setsockopt( mSocket, SOL_TCP, TCP_REPAIR, &aux, sizeof( aux )) < 0 )
reportError( "Tried to do a rude socket close... but could not turn on repair mode.\n" );
}
close( mSocket );
I'll report back if this works. (@edit: tested answer below)
@ The "leave the socket open" idea:
This works but is sub optimal. The attacker has the ability to saturate your system with open sockets. Each request creates a new socket that is left open. With DOS attack you eventually run out of sockets.
Then there is also the problem with managing the open sockets:
For most socket interfaces, the maximum number of sockets allowed per each connection between an application and the TCP/IP sockets interface is 65535.
Thanks :D. @premktiw: Yes, multiple client sockets can be bound to the same local IP/port pair at the same time, if they are connected to different server IP/Port pairs so the tuples of local+remote pairs are unique.
A TCP socket is a specific TCP port on a specific node. Port exhaustion occurs when a node runs out of available ports. When an application stops using a specific port, the port enters a "time-wait state" before it becomes available for use by another application.
Ok, did some research and I have an answer that works for me, based on TCP_REPAIR. It is a little more complex than I thought at first:
if ( abuse )
{
// read some bytes from the spammer - to establish the connection
u32 tries = 20;
while ( tries )
{
sleep( 1000 );
char tmpBuf[32];
s32 readCount = recv( mSocket, &tmpBuf[0], 32, 0 );
if ( readCount > -1 ) break;
tries--;
}
#ifdef TCP_REPAIR
int aux = 1;
if ( setsockopt( mSocket, SOL_TCP, TCP_REPAIR, &aux, sizeof( aux )) < 0 )
{
reportError( "could not turn on repair mode" );
}
#else // !TCP_REPAIR
// no TCP REPAIR - best we can do is an abort close
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
if ( setsockopt( mSocket, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger ) < 0 )
{
reportError( "Cannot turn off SO_LINGER" );
}
#endif // TCP_REPAIR
}
close( mSocket );
At the kernel level, the TCP stack will either send a FIN or RST packet if you close a connection, no matter how you do it ( with either close or shutdown ). Either way the attacker is notified that you closed the connection.
We want to silently close the connection and let them wait around to realize you're not answering... because we're vindictive.
TCP_REPAIR is a new socket API designed to allow you to 'freeze' a socket, save its state, and reload the socket state on another process or even another system. Under normal usage the client would never know their connection was transferred elsewhere.
But we can abuse this API, we put the socket in repair mode, but don't save its state and never restore it. When we close the socket in repair mode - it gets silently deleted.
This works for abusive requests that have already begun. That is we've read the spammer's request and decided it was fraud and TCP_REPAIR killed it.
But if you block requests by IP, right after connect, without first reading the socket, somehow the remote party is notified. They get an RST. Or probably something in the connection never quite completes and the remote system aborts the request almost immediately.
So we first read a few bytes from the hacker's socket. In my case the socket is already in non-blocking mode. But if not you want to set the socket to non-block, or else you open yourself up to the hacker opening connection, but sending no packets and leaving your server hanging - like you plan to do to him. If after a few microseconds you don't get a packet you shut him down anyway.
But if you read a few bytes from him, then his program is left waiting for a response from you that never comes.
TCP_REPAIR is only available on Linux Kernels 3.5 and above. Below that the best I can do is a 'dirty' socket close. This is where instead of sending him a FIN you send him and RST. It will look to him like a valid connection was never established. To do this, you turn off SO_LINGER, to essentially break the socket connection close handshake, and then call close.
Works like a charm, point your browser here:
http://oroboro.com/fail
Chrome at least will hang there for 5-10 seconds. Looking at my logs where I was getting 10 hits per second - he's only able to hit me every 10 seconds or so. Load on my system from this: 0.
See ya sucker!
When you detected a malicious client, I would recommend you to not just close the connection, but to also refuse any new connections originating from the same IP address.
What you can do at least is blacklist the IP in your application. Keep a list of banned IP addresses and immediately close any accepted socket which originates from an IP on that list.
But to protect more of your resources, it would be even better to block the connection further outward in the network architecture. When you are able to do so, notify the gateway router to block it. When that's impossible, try to get the load balancer to block it. Failing that, at least add a rule to the local firewall of the server.
But keep in mind that many such attacks originate from consumer-grade internet connections (with or without the user being aware). That usually means their IP addresses are assigned and regularly re-assigned dynamically. An IP which was used by a spammer a few days ago might now be used by a legitimate user. So IP-based bans shouldn't last forever.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With