Linux – (PCI-DSS, APF) Firewall UDP Packet Source Port 53 Ruleset Bypass

firewalliptableslinuxpci-dss

I am handling vulnerabilities reported by a PCI-DSS scanner, and one of them is new to me:

Title
Firewall UDP Packet Source Port 53 Ruleset Bypass
Synopsis:
Firewall rulesets can be bypassed.
Impact:
It is possible to bypass the rules of the remote firewall by sending UDP packets with a source port equal to 53. An attacker may use this flaw to inject UDP packets to the remote hosts, in spite of the presence of a firewall.
See also :
http://archives.neohapsis.com/archives/fulldisclosure/2003-q2/0352.html
http://www.nessus.org/u?4368bb37

The first linked article gives a proof of exploit command, nmap -v -P0 -sU -p 1900 ${IP} -g 53, which does in fact return one 56 byte packet if the source port is 53. But why? In this example, it reports port 1900 is "closed" but a 56 byte reply was returned. In contrast, a request to port 1900 with UDP source port 123 (also open) returns 0 bytes.

#
# Source port 53:
#
$ sudo nmap -v -P0 -sU -p 1900 ${IP} -g 53

Starting Nmap 6.47 ( http://nmap.org ) at 2015-11-24 20:19 CST
Initiating Parallel DNS resolution of 1 host. at 20:19
Completed Parallel DNS resolution of 1 host. at 20:19, 0.00s elapsed
Initiating UDP Scan at 20:19
Scanning *HOST* (*IP*) [1 port]
Completed UDP Scan at 20:19, 0.21s elapsed (1 total ports)
Nmap scan report for *HOST* (*IP*)
Host is up (0.038s latency).
PORT     STATE  SERVICE
1900/udp closed upnp

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.25 seconds
           Raw packets sent: 1 (28B) | Rcvd: 1 (56B)

#
# Source Port 123:
#
$ sudo nmap -v -P0 -sU -p 1900 ${IP} -g 123

Starting Nmap 6.47 ( http://nmap.org ) at 2015-11-24 20:20 CST
Initiating Parallel DNS resolution of 1 host. at 20:20
Completed Parallel DNS resolution of 1 host. at 20:20, 0.00s elapsed
Initiating UDP Scan at 20:20
Scanning *HOST* (*IP*) [1 port]
Completed UDP Scan at 20:20, 2.42s elapsed (1 total ports)
Nmap scan report for *HOST* (*IP*)
Host is up.
PORT     STATE         SERVICE
1900/udp open|filtered upnp

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 2.46 seconds
           Raw packets sent: 2 (56B) | Rcvd: 0 (0B)

This Linux server is running a control panel (InterWorx-CP) that is managing an APF installation, which in turn generates the iptables rules. Here they are:

$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere
REFRESH_TEMP  all  --  anywhere             anywhere
TALLOW     all  --  anywhere             anywhere
TGALLOW    all  --  anywhere             anywhere
TDENY      all  --  anywhere             anywhere
TGDENY     all  --  anywhere             anywhere
DROP       tcp  --  anywhere             anywhere            tcp dpts:epmap:netbios-ssn
DROP       udp  --  anywhere             anywhere            udp dpts:epmap:netbios-ssn
DROP       tcp  --  anywhere             anywhere            tcp dpt:sunrpc
DROP       udp  --  anywhere             anywhere            udp dpt:sunrpc
DROP       tcp  --  anywhere             anywhere            tcp dpt:login
DROP       udp  --  anywhere             anywhere            udp dpt:who
DROP       tcp  --  anywhere             anywhere            tcp dpt:microsoft-ds
DROP       udp  --  anywhere             anywhere            udp dpt:microsoft-ds
DROP       tcp  --  anywhere             anywhere            tcp dpt:ms-sql-s
DROP       udp  --  anywhere             anywhere            udp dpt:ms-sql-s
DROP       tcp  --  anywhere             anywhere            tcp dpt:ms-sql-m
DROP       udp  --  anywhere             anywhere            udp dpt:ms-sql-m
DROP       tcp  --  anywhere             anywhere            tcp dpt:search-agent
DROP       udp  --  anywhere             anywhere            udp dpt:search-agent
DROP       tcp  --  anywhere             anywhere            tcp dpt:ingreslock
DROP       udp  --  anywhere             anywhere            udp dpt:ingreslock
DROP       tcp  --  anywhere             anywhere            tcp dpt:ctx-bridge
DROP       udp  --  anywhere             anywhere            udp dpt:ctx-bridge
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ftp
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:lmtp
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:smtp
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:http
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:pop3
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:imap
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:https
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:imaps
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:pop3s
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:autodesk-nlm
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:powerclientcsf
ACCEPT     tcp  --  anywhere             anywhere            tcp dpts:50000:51000
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:submission
ACCEPT     udp  --  anywhere             anywhere            udp dpt:ftp-data
ACCEPT     udp  --  anywhere             anywhere            udp dpt:ftp
ACCEPT     udp  --  anywhere             anywhere            udp dpt:ntp
ACCEPT     udp  --  anywhere             anywhere            udp dpt:domain
ACCEPT     icmp --  anywhere             anywhere            icmp destination-unreachable limit: avg 14/sec burst 5
ACCEPT     icmp --  anywhere             anywhere            icmp redirect limit: avg 14/sec burst 5
ACCEPT     icmp --  anywhere             anywhere            icmp time-exceeded limit: avg 14/sec burst 5
ACCEPT     icmp --  anywhere             anywhere            icmp echo-reply limit: avg 14/sec burst 5
ACCEPT     icmp --  anywhere             anywhere            icmp type 30 limit: avg 14/sec burst 5
ACCEPT     icmp --  anywhere             anywhere            icmp echo-request limit: avg 14/sec burst 5
DROP       tcp  --  anywhere             anywhere            tcp flags:!FIN,SYN,RST,ACK/SYN state NEW
ACCEPT     tcp  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     udp  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     udp  --  anywhere             anywhere            udp spt:domain dpts:1023:65535
ACCEPT     tcp  --  anywhere             anywhere            tcp spt:domain dpts:1023:65535
DROP       tcp  --  anywhere             anywhere
DROP       udp  --  anywhere             anywhere
DROP       all  --  anywhere             anywhere

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere
TCPMSS     tcp  --  anywhere             anywhere            tcp flags:SYN,RST/SYN TCPMSS clamp to PMTU
REFRESH_TEMP  all  --  anywhere             anywhere
TALLOW     all  --  anywhere             anywhere
TGALLOW    all  --  anywhere             anywhere
TDENY      all  --  anywhere             anywhere
TGDENY     all  --  anywhere             anywhere
DROP       tcp  --  anywhere             anywhere            tcp dpts:epmap:netbios-ssn
DROP       udp  --  anywhere             anywhere            udp dpts:epmap:netbios-ssn
DROP       tcp  --  anywhere             anywhere            tcp dpt:sunrpc
DROP       udp  --  anywhere             anywhere            udp dpt:sunrpc
DROP       tcp  --  anywhere             anywhere            tcp dpt:login
DROP       udp  --  anywhere             anywhere            udp dpt:who
DROP       tcp  --  anywhere             anywhere            tcp dpt:microsoft-ds
DROP       udp  --  anywhere             anywhere            udp dpt:microsoft-ds
DROP       tcp  --  anywhere             anywhere            tcp dpt:ms-sql-s
DROP       udp  --  anywhere             anywhere            udp dpt:ms-sql-s
DROP       tcp  --  anywhere             anywhere            tcp dpt:ms-sql-m
DROP       udp  --  anywhere             anywhere            udp dpt:ms-sql-m
DROP       tcp  --  anywhere             anywhere            tcp dpt:search-agent
DROP       udp  --  anywhere             anywhere            udp dpt:search-agent
DROP       tcp  --  anywhere             anywhere            tcp dpt:ingreslock
DROP       udp  --  anywhere             anywhere            udp dpt:ingreslock
DROP       tcp  --  anywhere             anywhere            tcp dpt:ctx-bridge
DROP       udp  --  anywhere             anywhere            udp dpt:ctx-bridge
ACCEPT     tcp  --  anywhere             anywhere            tcp dpts:1024:65535 state RELATED,ESTABLISHED
ACCEPT     udp  --  anywhere             anywhere            udp dpts:1024:65535 state RELATED,ESTABLISHED
ACCEPT     udp  --  anywhere             anywhere            udp spts:1023:65535 dpt:domain
ACCEPT     tcp  --  anywhere             anywhere            tcp spts:1023:65535 dpt:domain
ACCEPT     all  --  anywhere             anywhere

Chain PROHIBIT (0 references)
target     prot opt source               destination
REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited

Chain REFRESH_TEMP (2 references)
target     prot opt source               destination

Chain RESET (0 references)
target     prot opt source               destination
REJECT     tcp  --  anywhere             anywhere            reject-with tcp-reset

Chain TALLOW (2 references)
target     prot opt source               destination

Chain TDENY (2 references)
target     prot opt source               destination

Chain TGALLOW (2 references)
target     prot opt source               destination

Chain TGDENY (2 references)
target     prot opt source               destination

The server is also a DNS authority for the domains it hosts, replicating to slave servers, so incoming DNS queries could be disabled. But even when I did that in the CP, the exploit still was successful. My guess is APF is generating some rules outside of my indirect control.

How do I go about closing this hole in the firewall?

What is the impact of this vulnerability from 2003, which the PCI scanner is just now reporting (years of scans already)?

Best Answer

They test with port 53 because it is likely open (i.e. port used by a DNS).

I got the same error and the solution was to write two rules.

First you can have an ESTABLISHED and RELATED rule for UDP now. That was not possible before since UDP is considered stateless, but they added that functionality by tracking what was sent and accept related replies.

-A INPUT -i eth0 -p udp -m state --state ESTABLISH,RELATED -m udp -d 1.2.3.4 -j ACCEPT

Note: change eth0 and 1.2.3.4 with proper name/IP

Then you can open port 53 for the DNS server incoming packets.

-A INPUT -i eth0 -p udp -m udp --dport 53 -d 1.2.3.4 -j ACCEPT

Note: change eth0 and 1.2.3.4 with proper name/IP

Your existing rule looks like this:

ACCEPT     udp  --  anywhere             anywhere            udp dpt:domain

It is not constrained on an interface or a destination address. It should be to make sure that you do not get data from a spurious source. (i.e. on DigitalOcean, and probably many others, there is a hidden IP address... you do not want to accept data from that one; also, I had a misshape once and the name of the interface changed!!! If you have a single network connection, it should be straight forward, but if you are not in control of the hardware, you cannot know when such may happen...)

That being said, your BIG problem in your ruleset is the very first line in your INPUT chain. It looks like this:

ACCEPT     all  --  anywhere             anywhere

And that means accept absolutely whatever. All the rules after that are all ignored. So in other words, you do not have a firewall at all...

You have the same first rule in your OUTPUT chain, I suppose that's to make really sure your firewall is not going to block anything.

A word of advise, write a small script to look at your firewall using the -nvx options. The -n makes it fast by not trying to convert IP addresses. The -v is to show you the number of packets and bytes traveling on each rule (i.e. if a rule accepts a packet, its packet counter is incremented by 1.) The -x shows you the exact numbers for each counter (instead of making it "human",) so that way I know when a counter was incremented by 1 or more. Important while you are testing.

If you had used the -nvx maybe you'd notice that only the counters of the very first rule were increment for the INPUT and the OUTPUT. Then maybe you'd wander why you never get hits against the other rules...