Finding connections to a specific port with SystemTap
Earlier in the week I was asked by someone if I know a way to monitor applications trying to connect to the telnet port. The obvious answer was netstat/lsof, but the problem was that this application would be up for merely seconds and he did not know when/where it started up. All he had was his telnetd log telling him about the connections. So I decided to go the SystemTap way and came up with this:
/* snoop.stp */ function sockaddr_to_port:long(sck:long) %{ short ret = 0; struct sockaddr *sock = (struct sockaddr *) (long) THIS->sck; memcpy(&ret, sock->sa_data+1, 1); memcpy(((char *)&ret)+1, sock->sa_data, 1); THIS->__retvalue = ret; %} global testport = 0 probe begin(0) { if (testport == 0) { printf("Usage staprun snoop.ko testport=<portnum>\n"); exit(); } } probe syscall.connect { port = sockaddr_to_port($uservaddr) if (port == testport) { printf ("%d: %s trying to connect to port %d\n", gettimeofday_s(), execname(), testport); printf ("\tPID: %d\n", pid()); printf ("\tUID: %d\n", uid()); printf ("\tEUID: %d\n", euid()); printf ("\tParent: %s\n", pexecname()); printf ("\tPPID: %d\n", ppid()); } }
So this really is a very simple tap on the connect syscall to be able to intercept all connect requests on the system. The sockaddr_to_port
is a little more interesting. It takes the first two bytes of sockaddr->sa_data and swaps them to get them to the correct byte order to return as the port number. The reason for the swap is that network byte ordering is essentially big endian (The most significant byte first) while x86 computers are little endian. So the port number 23, at byte level would be seen on your computer as:
0x00170000
while in network byte order it is seen as:
0x00000017
Once we have the port number, the rest is easy pickings with SystemTap giving us easy functions to collect the executable name, pid, parent process name, etc. As for the date, one may easily convert it to human readable form using:
date -d @<my date in seconds>
Now that we have all the pieces in place, we can build the above script into a kernel module using:
stap -vvvg -r `uname -r` snoop.stp -m snoop
The output of this is the kernel module named snoop.ko. The -g signifies the "Guru mode". We need it since our function sockaddr_to_port
is embedded C and we need to tell stap that we really know what we're doing. I like putting in a lot of v's in the first place so that I get build errors right away. This is essentially building a kernel module. The -r and -m are for kernel version number and module name respectively. Once built, you can deploy the module using:
staprun snoop.ko testport=23
This loads the module into the kernel and waits to print messages (whenever the program decides to connect to telnet) to standard output. If you look into the output of lsmod, you will find that snoop is a loaded module.
Packages you will need to implement all of this:
- systemtap
- systemtap-runtime
- kernel-debuginfo
- kernel-devel
- kernel-debuginfo-common
If you're using the PAE kernel then you need the kernel-PAE-debuginfo and kernel-PAE-devel instead of kernel-debuginfo and kernel-devel. If you're only looking to deploy a binary systemtap module, you will only need systemtap-runtime. Yes, I can run a module built on one system, on another system provided they have the same kernel version and architecture. But be careful of what you run, always inspect the source to make sure you're not running anything malicious.
Comments are closed.