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)?
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.
Note: change
eth0
and1.2.3.4
with proper name/IPThen you can open port 53 for the DNS server incoming packets.
Note: change
eth0
and1.2.3.4
with proper name/IPYour existing rule looks like this:
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:
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...