TCPdump DROP privileges

I’m sure many of you know tcpdump and use it frequently. As we all know when it is run without privileges to capture packets on a network interface it displays the following message:

$ /usr/sbin/tcpdump -i eth0
tcpdump: eth0: You don't have permission to capture on that device
(socket: Operation not permitted)

The reason is that the application requires certain privileges that our user does not have. This is the reason why tcpdump is normally executed as root, taking the risk that a vulnerability exploits the fact that we’re launching the application as a administrator user.

Doing a quick search in Google we can see several examples of tcpdump vulnerabilities in the Taosecurity blog, so there are not many, but there exist. We must be aware that the likelihood of this vulnerability exploitation is often low (due mainly to the maturity of the tool), but even so, is “always” recommended (or required) to execute any application with the minimum necessary privileges. In the next paragraphs we will see how to achieve this with tcpdump.

If we launch tcpdump as root and do a “ps” it shows the following (the owner of the process is root user):

$ps -ef 
root  	5445  5435  0 13:09 pts/0	00:00:00 tcpdump -i eth0 port 4444

However if we launch it with option -Z, that has the following meaning according to the man page:

-Z : Drops privileges (if root) and changes user ID to user and the group ID to 
the primary group of user. 

We get the following output (check the user owner of the process):

$ tcpdump -i eth0 port 4444 -Z josemi
$ ps -ef
josemi	 5450  5435  0 13:09 pts/0	00:00:00 tcpdump -i eth0 port 4444 -Z josemi

If we look at the status of this process it shows (check the uid and gid):

$cat /proc/5450/status
Name:    tcpdump
State:    S (sleeping)
Tgid:    5450
Pid:    5450
PPid:    5435
TracerPid:    0
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000

$ cat /etc/passwd | grep -i josemi

Checking the system calls done by tcpdump when we lauch it with the option -Z we see how it changes to the user “josemi”. To do this we will use strace:

$ strace tcpdump -i eth0 port 4444 -Z josemi

setgroups32(9, [1000, 4, 20, 24, 46, 112, 120, 122, 126]) = 0
setgid32(1000)                      	= 0
setuid32(1000)                      	= 0
write(2, "tcpdump: verbose output suppress"..., 75tcpdump: verbose output 
suppressed, use -v or -vv for full protocol decode) = 75
write(2, "listening on eth0, link-type EN1"..., 73listening on eth0, link-type 
EN10MB (Ethernet), capture size 65535 bytes) = 73
poll([{fd=3, events=POLLIN}], 1, 1000)  = 0 (Timeout)
poll([{fd=3, events=POLLIN}], 1, 1000)  = 0 (Timeout)
poll([{fd=3, events=POLLIN}], 1, 1000)  = 0 (Timeout)
poll([{fd=3, events=POLLIN}], 1, 1000 

The interesting system calls are setgid32 and setuid32. These two calls return 0 on success. The change is done after all operations have been run as superuser. Reading the documentation for these functions we can see the following:

“If the user is root or the program is set-user-ID-root, special care must be taken. The setuid() function checks the effective user ID of the caller and if it is the superuser, all process-related user ID’s are set to uid. After this has occurred, it is impossible for the program to regain root privileges.

Thus, a set-user-ID-root program wishing to temporarily drop root privileges, assume the identity of an unprivileged user, and then regain root privileges afterward cannot use setuid().”

I’ve highlighted the fact that if the user is root it won’t regain the original privileges. As we have seen the process belongs to the user josemi so in the case of an attacker exploiting a vulnerability s/he would get the privileges of this user and not the superuser.

As always we hope you find it useful. Any correction or comment is welcome ;-)