VPN traffic leaks in IPv6/IPv4 dual-stack networks/hosts

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

VPN traffic leaks in IPv6/IPv4 dual-stack networks/hosts

Fernando Gont-2
Folks,

FYI. This is might affect OpenBSD users employing e.g. OpenVPN:
<http://tools.ietf.org/html/draft-gont-opsec-vpn-leakages>.

For a project such as OpenVPN, a (portable) fix might be non-trivial.
However, I guess OpenBSD might hook some PF rules when establishing the
VPN tunnel, such that e.g. all v6 traffic is filtered (yes, this is
certainly not the most desirable fix, but still probably better than
having your supposedly-secured traffic being sent in the clear).

Thanks,
--
Fernando Gont
e-mail: [hidden email] || [hidden email]
PGP Fingerprint: 7809 84F5 322E 45C7 F1C9 3945 96EE A9EF D076 FFF1

Reply | Threaded
Open this post in threaded view
|

Re: VPN traffic leaks in IPv6/IPv4 dual-stack networks/hosts

Henning Brauer-5
* Fernando Gont <[hidden email]> [2012-11-23 12:09]:
> FYI. This is might affect OpenBSD users employing e.g. OpenVPN:
> <http://tools.ietf.org/html/draft-gont-opsec-vpn-leakages>.

we're way less affected than other OSes, since we prefer inet over
inet6 by default. targeted attacks could still work.

I recommend "ifconfig $foo -inet6" in any case :)

--
Henning Brauer, [hidden email], [hidden email]
BS Web Services, http://bsws.de, Full-Service ISP
Secure Hosting, Mail and DNS Services. Dedicated Servers, Root to Fully Managed
Henning Brauer Consulting, http://henningbrauer.com/

Reply | Threaded
Open this post in threaded view
|

Re: VPN traffic leaks in IPv6/IPv4 dual-stack networks/hosts

Fernando Gont-2
On 11/23/2012 08:44 AM, Henning Brauer wrote:
> * Fernando Gont <[hidden email]> [2012-11-23 12:09]:
>> FYI. This is might affect OpenBSD users employing e.g. OpenVPN:
>> <http://tools.ietf.org/html/draft-gont-opsec-vpn-leakages>.
>
> we're way less affected than other OSes, since we prefer inet over
> inet6 by default. targeted attacks could still work.

What about preference in terms of DNS transport? (e.g., when you've
learned recursive DNS servers by means of DHCPv4, but also by means of RAs?)

There could also be a scenario in which the attacker intentionally
disables the v4 connectivity, such that you only have v6.



> I recommend "ifconfig $foo -inet6" in any case :)

Is anything like this triggered by default along with the
tunnel-establishment process? The OpenVPN folks are aware about this
issue, but they are probably going to wait till the have some sort of
"portable" fix for this issue.

Cheers,
--
Fernando Gont
e-mail: [hidden email] || [hidden email]
PGP Fingerprint: 7809 84F5 322E 45C7 F1C9 3945 96EE A9EF D076 FFF1

Reply | Threaded
Open this post in threaded view
|

Re: VPN traffic leaks in IPv6/IPv4 dual-stack networks/hosts

Reyk Floeter-2
In reply to this post by Henning Brauer-5
On Fri, Nov 23, 2012 at 12:44:32PM +0100, Henning Brauer wrote:
> * Fernando Gont <[hidden email]> [2012-11-23 12:09]:
> > FYI. This is might affect OpenBSD users employing e.g. OpenVPN:
> > <http://tools.ietf.org/html/draft-gont-opsec-vpn-leakages>.
>
> we're way less affected than other OSes, since we prefer inet over
> inet6 by default. targeted attacks could still work.
>

Yes!

But this wouldn't help any non-OpenBSD hosts behind an OpenBSD VPN
gateway.  "block quick inet6" is more efficient in this case ;)

> I recommend "ifconfig $foo -inet6" in any case :)
>

In the section "Mitigations to VPN traffic-leakage vulnerabilities" of
Fernando's paper it is suggested that a VPN client disables IPv6
globally if it is not going to send all IPv6 traffic over the tunnel
as well.  The problems with -inet6 in this context are that a) it
isn't a global IPv6 on/off switch and b) that it deletes all
configured addresses from the selected interface, so the VPN client
could not restore the previous configuration after the VPN connection
is terminated.

In iked(8)'s case, IPv6 is supported but nothing is forwarded unless
the related flows are configured.  And even in this case, IKEv2 would
use traffic selector negotiation, which is not yet implemented in
iked, and disable or narrow the configured IPv6 flows based on the
peer's configuration.  Maybe traffic selector negotiation is a risk by
itself (see RFC 5996, section 2.9).

The mitigation section of the paper is not very clear about the "IPv6
supported, but not used or negotiated" case.  Regarding iked(8), I
would think about at least one of the following options:

a) Just don't care about it and expect that admins configured pf and
the interfaces correctly.

b) Document it in the iked.conf(5) manpage near the packet filtering sections.

c) Make sure that the "any" keyword for flows in iked.conf will cause
iked to advertise both the IPv4 and IPv6 traffic selectors.  This
would at least mitigate some possible problems, for example with the
Windows IKEv2 client which already asks for any IPv4 and IPv6 by
default.

d) Implement an automatic IPv6 kill switch in iked that follows the
suggestion to disable all IPv6 traffic if we have an IPv4-only tunnel.
iked(8) could use a pf anchor to load a block rule, disable
net.inet6.ip6.forwarding, or we could add a knob net.inet6.enable=0
that doesn't alter the configured routes and addresses and simply
returns somewhere in the network stack (ugh).

e) Just switch everything to IPv6 today.  Oh wait, OpenBSD would have
a reversed problem in this case by looking up IPv4 on dual-stack
setups first *g*.  Haha, I'm wondering if we'll ever see any serious
security problems because of "legacy IPv4" in an IPv6-world...

Reyk

Reply | Threaded
Open this post in threaded view
|

Re: VPN traffic leaks in IPv6/IPv4 dual-stack networks/hosts

Fernando Gont-2
On 11/23/2012 11:12 AM, Reyk Floeter wrote:
> In the section "Mitigations to VPN traffic-leakage vulnerabilities" of
> Fernando's paper it is suggested that a VPN client disables IPv6
> globally if it is not going to send all IPv6 traffic over the tunnel
> as well.  

The problem is that even if you tried to send all IPv6 traffic over the
VPN, the mechanism to achieve that might be non-trivial. e.g., some VPN
implementations install "more specific routes" that override (i.e
"longest matching prefix" thing) the existing default route. -- but
there are a plethora of vectors that might be leveraged to install even
more specific routes than those (Route Information Options, ICMPv6
Redirects, etc.).

That is, "getting all the v6 traffic on the VPN tunnel" might be tricky.



> a) Just don't care about it and expect that admins configured pf and
> the interfaces correctly.

This one doesn't seem to obey the principle of "least surprise". Many
people are not aware about the implications of v6 on their "v4-only"
networks.



> d) Implement an automatic IPv6 kill switch in iked that follows the
> suggestion to disable all IPv6 traffic if we have an IPv4-only tunnel.
> iked(8) could use a pf anchor to load a block rule, disable
> net.inet6.ip6.forwarding, or we could add a knob net.inet6.enable=0
> that doesn't alter the configured routes and addresses and simply
> returns somewhere in the network stack (ugh).

This seems a sensible thing to do.

Cheers,
--
Fernando Gont
e-mail: [hidden email] || [hidden email]
PGP Fingerprint: 7809 84F5 322E 45C7 F1C9 3945 96EE A9EF D076 FFF1

Reply | Threaded
Open this post in threaded view
|

Re: VPN traffic leaks in IPv6/IPv4 dual-stack networks/hosts

Reyk Floeter-2
On Fri, Nov 23, 2012 at 12:01:07PM -0300, Fernando Gont wrote:

> On 11/23/2012 11:12 AM, Reyk Floeter wrote:
> > In the section "Mitigations to VPN traffic-leakage vulnerabilities" of
> > Fernando's paper it is suggested that a VPN client disables IPv6
> > globally if it is not going to send all IPv6 traffic over the tunnel
> > as well.  
>
> The problem is that even if you tried to send all IPv6 traffic over the
> VPN, the mechanism to achieve that might be non-trivial. e.g., some VPN
> implementations install "more specific routes" that override (i.e
> "longest matching prefix" thing) the existing default route. -- but
> there are a plethora of vectors that might be leveraged to install even
> more specific routes than those (Route Information Options, ICMPv6
> Redirects, etc.).
>
> That is, "getting all the v6 traffic on the VPN tunnel" might be tricky.
>

Well, it is less tricky with our IPsec stack because we have these
crazy flows (aka. SPD) that always "magically" bypass the routing
table.  And I think the risk that someone sneaks in conflicting flows
is very small.

> > a) Just don't care about it and expect that admins configured pf and
> > the interfaces correctly.
>
> This one doesn't seem to obey the principle of "least surprise". Many
> people are not aware about the implications of v6 on their "v4-only"
> networks.
>

So how big is the risk with the link local addresses only?

Otherwise the risk in an IPv4-only network is relatively low, and it
would be a "surprising surprise", because we don't accept RAs by
default and the attacks in sections 4 + 5 of your paper would not work
(net.inet6.ip6.accept_rtadv=0 and net.inet6.icmp6.rediraccept=0).
Unless you can somehow send a user to a host in the local network
using the link local connectivity.

>
>
> > d) Implement an automatic IPv6 kill switch in iked that follows the
> > suggestion to disable all IPv6 traffic if we have an IPv4-only tunnel.
> > iked(8) could use a pf anchor to load a block rule, disable
> > net.inet6.ip6.forwarding, or we could add a knob net.inet6.enable=0
> > that doesn't alter the configured routes and addresses and simply
> > returns somewhere in the network stack (ugh).
>
> This seems a sensible thing to do.
>

Actually, in the iked(8)/IPsec case we could even block all v6 traffic
without using PF by simply inserting a single "deny" flow.
For example:

# ping6 -w ff02::1%em0
# ipsecctl -vf /etc/ipsec-block.conf
flow esp out from ::/0 to ::/0 type deny
# ping6 -w ff02::1%em0

Most IPsec VPN clients could use their existing PFKEYv2 interface to
dynamically add a similar rule to their Security Policy Database.  But
unfortunately, the SPD is the least portable part of PFKEYv2.

This would obviously not help with OpenVPN or PPP.  Some IPsec
implementations even use tun/tap-like userspace interfaces instead of
the IPsec stack (I think "vpnc" is such a case).

Reyk

Reply | Threaded
Open this post in threaded view
|

Re: VPN traffic leaks in IPv6/IPv4 dual-stack networks/hosts

Reyk Floeter-2
On Fri, Nov 23, 2012 at 05:01:16PM +0100, Reyk Floeter wrote:

> Actually, in the iked(8)/IPsec case we could even block all v6 traffic
> without using PF by simply inserting a single "deny" flow.
> For example:
>
> # ping6 -w ff02::1%em0
> # ipsecctl -vf /etc/ipsec-block.conf
> flow esp out from ::/0 to ::/0 type deny
> # ping6 -w ff02::1%em0
>
> Most IPsec VPN clients could use their existing PFKEYv2 interface to
> dynamically add a similar rule to their Security Policy Database.  But
> unfortunately, the SPD is the least portable part of PFKEYv2.
>

Putting it into practice, it could look like this.

iked(8) would block any IPv6 traffic by default unless an IPv6 flow is
loaded or the -6 command line flag is specified.  This diff is a PoC
and definitely needs more thoughts.

Anyone?

Reyk

Index: iked.8
===================================================================
RCS file: /cvs/src/sbin/iked/iked.8,v
retrieving revision 1.10
diff -u -p -r1.10 iked.8
--- iked.8 22 Oct 2012 13:27:23 -0000 1.10
+++ iked.8 23 Nov 2012 17:53:15 -0000
@@ -23,7 +23,7 @@
 .Nd Internet Key Exchange version 2 (IKEv2) daemon
 .Sh SYNOPSIS
 .Nm iked
-.Op Fl dnSTtv
+.Op Fl 6dnSTtv
 .Oo
 .Fl D Ar macro Ns = Ns Ar value
 .Oc
@@ -59,6 +59,13 @@ infrastructure.
 .Pp
 The options are as follows:
 .Bl -tag -width Ds
+.It Fl 6
+Disable automatic blocking of IPv6 traffic.
+By default,
+.Xr iked 8
+blocks any IPv6 traffic unless a flow for this address family has been
+negotiated.
+This option is used to prevent VPN traffic leakages on dual stack hosts.
 .It Fl D Ar macro Ns = Ns Ar value
 Define
 .Ar macro
Index: iked.c
===================================================================
RCS file: /cvs/src/sbin/iked/iked.c,v
retrieving revision 1.13
diff -u -p -r1.13 iked.c
--- iked.c 22 Oct 2012 10:25:17 -0000 1.13
+++ iked.c 23 Nov 2012 17:53:15 -0000
@@ -65,7 +65,7 @@ usage(void)
 {
  extern char *__progname;
 
- fprintf(stderr, "usage: %s [-dnSTtv] [-D macro=value] "
+ fprintf(stderr, "usage: %s [-6dnSTtv] [-D macro=value] "
     "[-f file]\n", __progname);
  exit(1);
 }
@@ -82,8 +82,11 @@ main(int argc, char *argv[])
 
  log_init(1);
 
- while ((c = getopt(argc, argv, "dD:nf:vSTt")) != -1) {
+ while ((c = getopt(argc, argv, "6dD:nf:vSTt")) != -1) {
  switch (c) {
+ case '6':
+ opts |= IKED_OPT_NOIPV6BLOCKING;
+ break;
  case 'd':
  debug++;
  break;
Index: iked.h
===================================================================
RCS file: /cvs/src/sbin/iked/iked.h,v
retrieving revision 1.54
diff -u -p -r1.54 iked.h
--- iked.h 22 Oct 2012 10:25:17 -0000 1.54
+++ iked.h 23 Nov 2012 17:53:16 -0000
@@ -141,6 +141,7 @@ struct iked_flow {
 
  u_int8_t flow_saproto;
  u_int8_t flow_ipproto;
+ u_int8_t flow_type;
 
  struct iked_id *flow_srcid;
  struct iked_id *flow_dstid;
@@ -762,6 +763,7 @@ int eap_parse(struct iked *, struct ike
 int pfkey_couple(int, struct iked_sas *, int);
 int pfkey_flow_add(int fd, struct iked_flow *);
 int pfkey_flow_delete(int fd, struct iked_flow *);
+int pfkey_block(int, int, u_int);
 int pfkey_sa_init(int, struct iked_childsa *, u_int32_t *);
 int pfkey_sa_add(int, struct iked_childsa *, struct iked_childsa *);
 int pfkey_sa_delete(int, struct iked_childsa *);
Index: pfkey.c
===================================================================
RCS file: /cvs/src/sbin/iked/pfkey.c,v
retrieving revision 1.20
diff -u -p -r1.20 pfkey.c
--- pfkey.c 23 Oct 2012 14:40:14 -0000 1.20
+++ pfkey.c 23 Nov 2012 17:53:18 -0000
@@ -48,7 +48,9 @@
 
 static u_int32_t sadb_msg_seq = 0;
 static u_int sadb_decoupled = 0;
+static u_int sadb_ipv6refcnt = 0;
 
+static int pfkey_blockipv6 = 0;
 static struct event pfkey_timer_ev;
 static struct timeval pfkey_timer_tv;
 
@@ -247,7 +249,7 @@ pfkey_flow(int sd, u_int8_t satype, u_in
 
  bzero(&slocal, sizeof(slocal));
  bzero(&speer, sizeof(speer));
- if (action != SADB_X_DELFLOW) {
+ if (action != SADB_X_DELFLOW && flow->flow_local != NULL) {
  memcpy(&slocal, &flow->flow_local->addr, sizeof(slocal));
  socket_af((struct sockaddr *)&slocal, 0);
 
@@ -268,8 +270,9 @@ pfkey_flow(int sd, u_int8_t satype, u_in
  sa_flowtype.sadb_protocol_len = sizeof(sa_flowtype) / 8;
  sa_flowtype.sadb_protocol_direction = flow->flow_dir;
  sa_flowtype.sadb_protocol_proto =
-    flow->flow_dir == IPSP_DIRECTION_IN ?
-    SADB_X_FLOW_TYPE_USE : SADB_X_FLOW_TYPE_REQUIRE;
+    flow->flow_type ? flow->flow_type :
+    (flow->flow_dir == IPSP_DIRECTION_IN ?
+    SADB_X_FLOW_TYPE_USE : SADB_X_FLOW_TYPE_REQUIRE);
 
  bzero(&sa_protocol, sizeof(sa_protocol));
  sa_protocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
@@ -295,7 +298,7 @@ pfkey_flow(int sd, u_int8_t satype, u_in
  sa_dmask.sadb_address_len =
     (sizeof(sa_dmask) + ROUNDUP(dmask.ss_len)) / 8;
 
- if (action != SADB_X_DELFLOW) {
+ if (action != SADB_X_DELFLOW && flow->flow_local != NULL) {
  /* local address */
  bzero(&sa_local, sizeof(sa_local));
  sa_local.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
@@ -330,7 +333,7 @@ pfkey_flow(int sd, u_int8_t satype, u_in
  smsg.sadb_msg_len += sa_flowtype.sadb_protocol_len;
  iov_cnt++;
 
- if (action != SADB_X_DELFLOW) {
+ if (action != SADB_X_DELFLOW && flow->flow_local != NULL) {
 #if 0
  /* local ip */
  iov[iov_cnt].iov_base = &sa_local;
@@ -1039,6 +1042,13 @@ pfkey_flow_add(int fd, struct iked_flow
  return (-1);
 
  flow->flow_loaded = 1;
+
+ if (flow->flow_dst.addr_af == AF_INET6) {
+ sadb_ipv6refcnt++;
+ if (sadb_ipv6refcnt == 1)
+ return (pfkey_block(fd, AF_INET6, SADB_X_DELFLOW));
+ }
+
  return (0);
 }
 
@@ -1057,6 +1067,43 @@ pfkey_flow_delete(int fd, struct iked_fl
  return (-1);
 
  flow->flow_loaded = 0;
+
+ if (flow->flow_dst.addr_af == AF_INET6) {
+ sadb_ipv6refcnt--;
+ if (sadb_ipv6refcnt == 0)
+ return (pfkey_block(fd, AF_INET6, SADB_X_ADDFLOW));
+ }
+
+ return (0);
+}
+
+int
+pfkey_block(int fd, int af, u_int action)
+{
+ struct iked_flow flow;
+
+ if (!pfkey_blockipv6)
+ return (0);
+
+ /*
+ * Prevent VPN traffic leakages in dual-stack hosts/networks.
+ * http://tools.ietf.org/html/draft-gont-opsec-vpn-leakages.
+ * We forcibly block IPv6 traffic unless it is used in any of
+ * the flows by tracking a sadb_ipv6refcnt reference counter.
+ */
+ bzero(&flow, sizeof(flow));
+ flow.flow_src.addr_af = flow.flow_src.addr.ss_family = af;
+ flow.flow_src.addr_net = 1;
+ socket_af((struct sockaddr *)&flow.flow_src.addr, 0);
+ flow.flow_dst.addr_af = flow.flow_dst.addr.ss_family = af;
+ flow.flow_dst.addr_net = 1;
+ socket_af((struct sockaddr *)&flow.flow_dst.addr, 0);
+ flow.flow_type = SADB_X_FLOW_TYPE_DENY;
+ flow.flow_dir = IPSP_DIRECTION_OUT;
+
+ if (pfkey_flow(fd, SADB_SATYPE_ESP, action, &flow) == -1)
+ return (-1);
+
  return (0);
 }
 
@@ -1259,6 +1306,14 @@ pfkey_init(struct iked *env, int fd)
  pfkey_timer_tv.tv_sec = 1;
  pfkey_timer_tv.tv_usec = 0;
  evtimer_set(&pfkey_timer_ev, pfkey_timer_cb, env);
+
+ if (env->sc_opts & IKED_OPT_NOIPV6BLOCKING)
+ return;
+
+ /* Block all IPv6 traffic by default */
+ pfkey_blockipv6 = 1;
+ if (pfkey_block(fd, AF_INET6, SADB_X_ADDFLOW))
+ fatal("pfkey_init: failed to block IPv6 traffic");
 }
 
 void *
Index: types.h
===================================================================
RCS file: /cvs/src/sbin/iked/types.h,v
retrieving revision 1.15
diff -u -p -r1.15 types.h
--- types.h 23 Oct 2012 14:36:18 -0000 1.15
+++ types.h 23 Nov 2012 17:53:18 -0000
@@ -44,6 +44,7 @@
 #define IKED_OPT_NONATT 0x00000004
 #define IKED_OPT_NATT 0x00000008
 #define IKED_OPT_PASSIVE 0x00000010
+#define IKED_OPT_NOIPV6BLOCKING 0x00000020
 
 #define IKED_IKE_PORT 500
 #define IKED_NATT_PORT 4500