Vulnerability: The help function in net/netfilter/nf_nat_irc.c in the Linux kernel before 3.12.8 allows remote attackers to obtain sensitive information from kernel memory by establishing an IRC DCC session in which incorrect packet data is transmitted during use of the NAT mangle feature.
Vulnerability ID: CVE-2014-1690
CWE: CWE-200
Severity score: 2.9
Project: https://github.com/torvalds/linux
Published: 2014-02-28
File: net/netfilter/nf_nat_irc.c
Commit message: netfilter: nf_nat: fix access to uninitialized buffer in IRC NAT helper

Commit 5901b6be885e attempted to introduce IPv6 support into
IRC NAT helper. By doing so, the following code seemed to be removed
by accident:

  ip = ntohl(exp->master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip);
  sprintf(buffer, "%u %u", ip, port);
  pr_debug("nf_nat_irc: inserting '%s' == %pI4, port %u\n", buffer, &ip, port);

This leads to the fact that buffer[] was left uninitialized and
contained some stack value. When we call nf_nat_mangle_tcp_packet(),
we call strlen(buffer) on excatly this uninitialized buffer. If we
are unlucky and the skb has enough tailroom, we overwrite resp. leak
contents with values that sit on our stack into the packet and send
that out to the receiver.

Since the rather informal DCC spec [1] does not seem to specify
IPv6 support right now, we log such occurences so that admins can
act accordingly, and drop the packet. I've looked into XChat source,
and IPv6 is not supported there: addresses are in u32 and print
via %u format string.

Therefore, restore old behaviour as in IPv4, use snprintf(). The
IRC helper does not support IPv6 by now. By this, we can safely use
strlen(buffer) in nf_nat_mangle_tcp_packet() and prevent a buffer
overflow. Also simplify some code as we now have ct variable anyway.

  [1] http://www.irchelp.org/irchelp/rfc/ctcpspec.html

Fixes: 5901b6be885e ("netfilter: nf_nat: support IPv6 in IRC NAT helper")
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Cc: Harald Welte <laforge@gnumonks.org>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Commit author: Daniel Borkmann

Vulnerable version (before fix):
 static unsigned int help(struct sk_buff *skb, 			 struct nf_conntrack_expect *exp) { 	char buffer[sizeof("4294967296 65635")]; 	u_int16_t port; 	unsigned int ret;  	/* Reply comes from server. */ 	exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; 	exp->dir = IP_CT_DIR_REPLY; 	exp->expectfn = nf_nat_follow_master; static unsigned int help(struct sk_buff *skb, 	}  	if (port == 0) { 		nf_ct_helper_log(skb, exp->master, "all ports in use"); 		return NF_DROP; 	}  	ret = nf_nat_mangle_tcp_packet(skb, exp->master, ctinfo, 				       protoff, matchoff, matchlen, buffer, 				       strlen(buffer)); 	if (ret != NF_ACCEPT) { 		nf_ct_helper_log(skb, exp->master, "cannot mangle packet"); 		nf_ct_unexpect_related(exp); 	} 	return ret; } 

Fixed version (after fix):
 static unsigned int help(struct sk_buff *skb, 			 struct nf_conntrack_expect *exp) { 	char buffer[sizeof("4294967296 65635")]; 	struct nf_conn *ct = exp->master; 	union nf_inet_addr newaddr; 	u_int16_t port; 	unsigned int ret;  	/* Reply comes from server. */ 	newaddr = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3;  	exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; 	exp->dir = IP_CT_DIR_REPLY; 	exp->expectfn = nf_nat_follow_master; static unsigned int help(struct sk_buff *skb, 	}  	if (port == 0) { 		nf_ct_helper_log(skb, ct, "all ports in use"); 		return NF_DROP; 	}  	/* strlen("\1DCC CHAT chat AAAAAAAA P\1\n")=27 	 * strlen("\1DCC SCHAT chat AAAAAAAA P\1\n")=28 	 * strlen("\1DCC SEND F AAAAAAAA P S\1\n")=26 	 * strlen("\1DCC MOVE F AAAAAAAA P S\1\n")=26 	 * strlen("\1DCC TSEND F AAAAAAAA P S\1\n")=27 	 * 	 * AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits, 	 *                        255.255.255.255==4294967296, 10 digits) 	 * P:         bound port (min 1 d, max 5d (65635)) 	 * F:         filename   (min 1 d ) 	 * S:         size       (min 1 d ) 	 * 0x01, \n:  terminators 	 */ 	/* AAA = "us", ie. where server normally talks to. */ 	snprintf(buffer, sizeof(buffer), "%u %u", ntohl(newaddr.ip), port); 	pr_debug("nf_nat_irc: inserting '%s' == %pI4, port %u\n", 		 buffer, &newaddr.ip, port);  	ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, matchoff, 				       matchlen, buffer, strlen(buffer)); 	if (ret != NF_ACCEPT) { 		nf_ct_helper_log(skb, ct, "cannot mangle packet"); 		nf_ct_unexpect_related(exp); 	}  	return ret; } 

Patch:
patch: @@ -34,10 +34,14 @@ static unsigned int help(struct sk_buff *skb,
 			 struct nf_conntrack_expect *exp)
 {
 	char buffer[sizeof("4294967296 65635")];
+	struct nf_conn *ct = exp->master;
+	union nf_inet_addr newaddr;
 	u_int16_t port;
 	unsigned int ret;
 
 	/* Reply comes from server. */
+	newaddr = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3;
+
 	exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
 	exp->dir = IP_CT_DIR_REPLY;
 	exp->expectfn = nf_nat_follow_master;
@@ -57,17 +61,35 @@ static unsigned int help(struct sk_buff *skb,
 	}
 
 	if (port == 0) {
-		nf_ct_helper_log(skb, exp->master, "all ports in use");
+		nf_ct_helper_log(skb, ct, "all ports in use");
 		return NF_DROP;
 	}
 
-	ret = nf_nat_mangle_tcp_packet(skb, exp->master, ctinfo,
-				       protoff, matchoff, matchlen, buffer,
-				       strlen(buffer));
+	/* strlen("\1DCC CHAT chat AAAAAAAA P\1\n")=27
+	 * strlen("\1DCC SCHAT chat AAAAAAAA P\1\n")=28
+	 * strlen("\1DCC SEND F AAAAAAAA P S\1\n")=26
+	 * strlen("\1DCC MOVE F AAAAAAAA P S\1\n")=26
+	 * strlen("\1DCC TSEND F AAAAAAAA P S\1\n")=27
+	 *
+	 * AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits,
+	 *                        255.255.255.255==4294967296, 10 digits)
+	 * P:         bound port (min 1 d, max 5d (65635))
+	 * F:         filename   (min 1 d )
+	 * S:         size       (min 1 d )
+	 * 0x01, \n:  terminators
+	 */
+	/* AAA = "us", ie. where server normally talks to. */
+	snprintf(buffer, sizeof(buffer), "%u %u", ntohl(newaddr.ip), port);
+	pr_debug("nf_nat_irc: inserting '%s' == %pI4, port %u\n",
+		 buffer, &newaddr.ip, port);
+
+	ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, matchoff,
+				       matchlen, buffer, strlen(buffer));
 	if (ret != NF_ACCEPT) {
-		nf_ct_helper_log(skb, exp->master, "cannot mangle packet");
+		nf_ct_helper_log(skb, ct, "cannot mangle packet");
 		nf_ct_unexpect_related(exp);
 	}
+
 	return ret;
 }
path: net/netfilter/nf_nat_irc.c