Iptables Output rules are as important as the input ones

The wrong assertion

When securing a GNU/Linux server of any sort, one tends to carefully protect carefully any open port by means of enforcing some relatively complex iptables input rules. This way, we can make sure that any unprotected service will not be visible from the outside world. As a trivial example, if we are running a GNU/Linux web server and it does have a secure shell service running in order to administer it, we can just close the 22/tcp port to the outside world by implementing a trivial iptables input rule. So far so good but … what about the output chain? Most system administrator tend to forget about it completely. One erroneously assumes that by protecting the input, it is unnecessary to worry about the output. Wrong. As this post will show next, it is equally important to ensure that our output chain only does whatever it needs to, no more and no less.

A Case-study: XML-SOAP Injection

Let’s assume we have found a vulnerable web server with an XML-SOAP Injection flaw. Though this kind of vulnerability is rare today, from time to time some old developed SOAP services are still vulnerable and can be successfully exploited. This sort of attack, depending on the nature of the SOAP service implementation, can lead to:

  • Port-scan other hosts all across the Internet.
  • Attack other hosts all across the Internet by crafting malicious http requests (SQL-i, more XML-SOAP Injection, and the like) on behalf of the vulnerable server.
  • Read some protected files from the vulnerable server (i.e: /etc/passwd, and the like).
  • Provoke a DoS on the vulnerable server.

 These are the most common vector attacks that there are when exploiting an XML-SOAP Injection, however there could certainly be more.

During my research, I found a vulnerable server I had to patch. This server implemented a SOAP service designed to validate a license key for a protected piece of software. Any request to this server was made by means of issuing an HTTP request with all the necessary parameters inside the XML-encoded body. For security reasons, what follows is neither the accurate XML code nor the exact URL-patterns from the real vulnerable server:

<!--?xml version="1.0" encoding="UTF-8"?-->
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
         xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xs    i="http://www.w3.org/2001/XMLSchema-instance">
   <ns1:String_1 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">LICENSE_KEY</ns1:String_1>

Let’s have a look at the String_1 element’s node. According to the XML standard, it should be feasible to replace the literal LICENSE_KEY by a variable to be resolved dynamically by the server, this way:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe "http://disbauxes.upc.es:443">]>
<ns1:String_1 xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">&xxe;</ns1:String_1>

Therefore, although this particular SOAP injection was not reflecting the server’s output back to the user’s browser, it was still feasible to exploit it in order to:

  • Port-scan other hosts all across the Internet.
  • Infer what sort of important files where stored on the vulnerable server and their permissions.
  • Probably – although not tested -, provoke a DoS on the vulnerable server by reading the /dev/random special device.

The exploit

I wrote a PoC in Python to automate the exploitation. So, basically, what I wrote was a port-scanner and file-inferencer. Therefore, whenever I need to port-scan other hosts, and if I don’t want to be seen from those hosts, I can use this vulnerable server. This is so because this vulnerable server is not filtering its output at all, nor there is a firewall doing so. Let’s have a look at two outputs of my python script; the first one shows how it can be feasible to determine whether a certain file does exist and if the server http process effective user has read permissions over it or not:

./poc.py -f /etc/shadow
XML-SOAP Injector Port Scanner, 2015 by T.Castillo.
Using vulnerable server: [vulnerable_server]
Vulnerable SOAP service: [vulnerable_server/vulnerable_soap_url?wsdl]
Target will be: [vulnerable_server] Ports: [80]
/etc/shadow: exists and it is not READABLE

The next output comes from port-scanning my own web-server using the vulnerable one:

./poc.py -t disbauxes.upc.es -p 80,443
XML-SOAP Injector Port Scanner, 2015 by T. Castillo.
Using vulnerable server: [vulnerable_server]
Vulnerable SOAP service: [vulnerable_server/vulnerable_soap_url?wsdl]
Target will be: [disbauxes.upc.es] Ports: [80,443]
disbauxes.upc.es:80: OPEN
disbauxes.upc.es:443: OPEN

The XML-SOAP Injection vulnerability can allow, under some circumstances, to achieve more that what we have just shown, of course. And yet, in this particular and real case, it would be far more difficult to port-scan remote hosts should we have an iptables output rule different than PERMIT.

Basic iptables output rules to mitigate the port-scan exploitation

In this real case-study, one thing one could do (apart from doing the obvious thing of fixing the XML-SOAP Injection, of course!) would be to secure the server’s output to prevent arbitrary connections to remote servers. Of course, this makes sense in this particular scenario because the attacker cannot escalate privileges by XML-Injecting code. Otherwise, it would be pointless, eventually, because the attacker could just re-write the rules. So, in case all we need to allow is packets from a previously related or established client connection and maybe an occasional connection to resolve domain names to ip addresses or the other way round to our domain name server, this could perfectly do:

-A OUTPUT -p tcp -m state –state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp -m tcp -d our_dns_server_ip –dport 53 -j ACCEPT

And finally, don’t forget to establish the default rule to DROP:

# iptables -P OUTPUT DROP