CUPS Vulnerability
Recently, evilsocket released a document about remotely exploiting the CUPS service with root privileges. I’m going to take this opportunity to focus on this at a network level because… Well… That’s what I do. Hopefully you’ll find it useful and informative.
First and foremost, shout-out to evilsocket for discovering this. Second, at the time of this writing, there’s no exploit. Lastly, I’m focusing on the network portion so once an exploit is released, I still won’t be covering it as that’s not my skillset. I figure I should point this out to save you from reading any more in case you were just after an exploit. If you’re interested in the network aspect (including how to detect), keep reading. If not, have a good one and thanks for stopping in. :)
What’s CUPS?
Programmers love acronyms which spell things, this is the Common Unix Printing System and, as you can imagine, helps out with printing. It has a web-based interface for both printer and document management as well as having the ability to automatically add printers to your system, much like other operating systems. With ease of use comes the potential for security issues and this is one of them.
Network services at Layer-4 (TCP and UDP) require a port to be bound to for clients to connect to. We have 65,536 (if 0 counts) to choose from and some services are commonly bound to specific ports. For example, SSH is usually bound to TCP/22 but you can specify any port in the configuration file. CUPS is bound to TCP/631 and UDP/631 for management which, again, can be changed but for the most part, nobody really does.
It’s also important to note that we’re talking about two different Layer-4 protocols: TCP which is IP/6 and UDP which is IP/17 so keep that in mind. I’ll make sure to draw attention to each of these as we go along.
When binding a service to a port, a few things happen. First, the application requests a port on a protocol to bind to. The operating system then creates a socket (which is also a file in a way) and ties that socket to the port requested. Again, I’m not going to go too deep on this topic to keep things relatively simple. Once the operating system has provided the port to the application, you can then send data into that port for the application to use. During the binding process, the application requests (with assistance from the operating system) which interface to bind to. For instance, there is always a loopback adapter on all operating systems (127.0.0.1/8 and ::1/128) and there will likely be (at least) one more interface like an Ethernet connection or a WiFi connection. So how do we tell where the application is listening?
Using the “netstat” command, we’re able to poll different types of information about our network sockets. While there are many different types of information we can glean, we’re going to focus on TCP and UDP for this situation. In Figure 1, you can see the non-redacted information showing that “cupsd” is listening on TCP/631 and is bound to 127.0.0.1 and ::1 for IPv4 and IPv6 respectively. This tells us that nobody from another host can reach this service as the loopback adapter can’t be reached that way. Looking at the “SSH” service, we can see it listening on TCP/22 but the interface is 0.0.0.0 and :: — What does this mean?
Using 0.0.0.0/0 and ::/0 indicate to that the service is listening on every possible logical interface on the system. Do you have three VLAN interfaces? SSH is listening on those. VPN connected interface? It’s there as well.
So far, we can see that CUPS is listening locally only so what’s the big deal?
Let’s look at UDP. UDP is considered a stateless protocol so there’s no “LISTEN” or “SYN_SENT” states for it to be in which makes our command a little different.
When we take a look for CUPS on UDP, we can see it’s listening but there are a few differences to take note of: First, there’s no IPv6 configuration which makes me a sad panda but more importantly is the binding is set to 0.0.0.0 meaning this service is accessible by all hosts (ignoring firewalling) which brings us to the vulnerability.
According to evilsocket, UDP is the exploit vector which, when a certain packet arrives on the UDP port, the target machine then sends a TCP connection to a host with the information contained in the payload. Here’s what that looks like:
You’ll see here that we’ve created a specific line to be sent to our target and stored the line in a file called “payload.txt” which we’ll use with the wonderful hping3 tool. If you’ve used hping3 before, you’ll notice that there’s no reply but don’t worry, we’ll get to that. By setting our payload to include our call-back host, the target will then create a TCP session back to the host and port specified in the payload:
As you can see, by setting up a TCP listener using “nc”, the target successfully connected back to us and provided information. Using “tcpdump”, we can see the traffic coming back to us as well:
By running the packet capture and the fact that the reply packet doesn’t use TLS, we can see the raw data which shows us the same information from the “nc” command but at a network level.
So how do we stay safe against this? Firewalling and microsegmentation with PVLAN configurations. Oh, and disabling services you don’t need. If you don’t need CUPS, then uninstall it. If, for some reason, you’re not able to uninstall it, then disable the service and prevent it from starting during boot-time. You’ll want to consult the documentation from your distribution on how to do that.
Next, let’s continue with the host and work our way outward. Configure CUPS to only listen on the loopback adapter or, if you need CUPS in a networked environment, add host firewall rules to only allow traffic from trusted hosts to prevent internal threat actors from abusing this.
Looking one step up at the switch level, you should try and utilize PVLAN configurations whenever possible. While this does add more to initial management of the switch, it will ensure that hosts within the same Layer-2 broadcast domain will be able to communicate with one another.
Moving forward are Internal Segmentation Firewall (ISFW) devices. Threats from internal are more dangerous (usually) than external threats and having ISFWs set up for subnet traversal is a great way to help protect your internal assets against this and other internal threats.
One big prevention is making sure that your border firewalls only allow out what you need. Having “Internal -> Internet -> Any -> Any -> Accept” is a garbage rule and will come back to bite you in the ass. Have internal DNS and only allow your DNS servers to use DNS outbound. Do you have proxy servers? Only allow them HTTP/S outbound. You get the idea.
Last, and certainly not least — Don’t put machines directly on the Internet. C’mon now.
Detection
Create a payload file for you detection machine. Let’s say it’s 10.20.30.40 in our example and use a port that’s open across your network. If there is no such port, pick a port and open up your ISFWs to allow it through.
0 3 http://10.20.30.40:1234/printers/whatever
Next, put a list of all IP addresses of your possible CUPS servers in another file, line-by-line.
10.10.10.10
10.20.10.10
10.30.10.10
Open up two terminals/shells on your detection machine. On the first one, start your listener with:
nc -lnvp 1234
Replace “1234” with the port you chose earlier.
Lastly, hit all your CUPS servers with the payload with this one-liner:
for line in $(cat ./input.list); do hping3 -2 -E ./payload.txt -d 1500 -p 631 -c 1 $line; done
Take a look at your “nc” terminal and anything that reports back with:
connect to [172.17.1.154] from (UNKNOWN) [172.17.1.107] 56668
is susceptible. Keep in mind that “172.17.1.154” will be replaced with “10.20.30.40”, “172.17.1.107” will be the susceptible device and the high-port (56668 in this case) will always be different.