Credit: Wikipedia
OpenVPN’s use of Netfilter makes it susceptible to several attacks that can cause denial-of-service, deanonymization of clients, or redirection of a victim client connection to an attacker controlled server. Netfilter is a module within the Linux kernel that implements stateless and stateful firewall mechanisms and network address translation (NAT). Netfilter’s design includes hooks that are called at various points in the networking code to execute, e.g., user-defined firewall rules and NAT code. The NAT portion of Netfilter is designed in such a way that if a machine behind the NAT uses the same source port as an application listening on the same port as the NAT (i.e., the NAT is acting both as a NAT-router and a server), then Netfilter translates and routes received packets intended for the NAT’s own listening port to a host behind the NAT using the same port. This shadowing behavior is not specified in any relevant request for comments (rfc768, rfc793, rfc4787, rfc5382, rfc7857, or any of their successors). The remainder of this disclosure uses the term “port shadow(ing)” when discussing this attack primitive.
Port shadowing’s root cause originates from Netfilter’s lack of coordination with the Linux socket infrastructure to determine whether a port in a listening state (or any particular state) on the NAT creates ambiguity with a machine using the same port behind the NAT. Port shadowing has interesting implications for applications, such as OpenVPN, that rely on Netfilter for NAT. A malicious OpenVPN client can use port shadowing to deanonymize victim machines connected to the same OpenVPN server or escalate privileges from an OpenVPN client to a man-in-the-middle (c2mitm) between another client (the victim) and the OpenVPN server to which both the attacker and victim are connected. The c2mitm variation of the attack can be combined with a recently disclosed, server-side attack against OpenVPN to inject DNS responses, reset, or even hijack TCP connections of the victim, even when that connection is tunneled through the OpenVPN server.
Version information is at the bottom of this note. While we have not
yet tested other versions of OpenVPN, any version of Wireguard or strongSwan,
or any other VPN server implementations, we believe our attack applies to any
version of any VPN that relies on Linux’s Netfilter for NAT. We have
successfully tested the full FreeBSD-13 with natd and OpenVPN. Additionally,
we have tested against FreeBSD-13 with IPFW, PF, and IPF and found that, while
they are not succesptable to c2mitm, the port shadow still applies. There is no
specific implemetation bug with Netfilter, nor presumably FreeBSD’s NAT
implementations, that makes port shadowing possible, it is simply an
implementation artifact that is apropos of following the RFCs that relate to
NAT.
The port shadow attack primitive is not specific to OpenVPN and is fundamentally related to network address translation. OpenVPN is tightly coupled with the concept of NAT and depends on Netfilter for NAT support, hence, the following discussion about OpenVPN focuses on its role as a NAT since our attack arises from NAT related implementation details. Cryptography is not described in any detail since our attack does not target any cryptographic components. The following background subsections provide information on relevant threat models, OpenVPN, Netfilter, and the port shadow primitive.
The two threat models most relevant to this disclosure are adjacent and in-path attackers. Building a port shadow assumes a logically adjacent attacker and the DNS injection, the coup de grace covered at the end of this post, requires an in-path attacker.
MITRE defines a logically adjacent attacker as follows under the attack vector description MITRE:
The vulnerable component is bound to the network stack, but the attack is limited at the protocol level to a logically adjacent topology. This can mean an attack must be launched from the same shared physical (e.g., Bluetooth or IEEE 802.11) or logical (e.g., local IP subnet) network, or from within a secure or otherwise limited administrative domain (e.g., MPLS, secure VPN to an administrative network zone). One example of an Adjacent attack would be an ARP (IPv4) or neighbor discovery (IPv6) flood leading to a denial of service on the local LAN segment (e.g., CVE‑2013‑6014).
|OpenVPN Server(NAT/router)|
/ \
. .
/ \
|routerA| |routerV|
| |
. .
/ \
|attacker| |victim|
It is important to note that logically adjacent and network adjacent are two different threat models. Our attacks do not make any assupmtions about the attacker being network adjacent. In the context of our attacks, for practical purposes logically adjacent simply means the attacker is connected as a client to the same VPN server as the victim.
The in-path attacker is a machine that routes packets between two hosts. For this disclosure, the attacker is between the victim and the OpenVPN server. Readers seeking a formal definition of in-path threats or interesting in-path attacks are referred to Marczak et al.’s work on the Great Cannon.
|OpenVPN Server(NAT/router)|
|
.
|attacker|
|
.
|
|victim|
An attacker can leverage the port shadow primitive to escalate from logically adjacent to in-path against OpenVPN.
OpenVPN uses Netfilter to perform NAT for clients connected to the server. A non-exhaustive list of VPN use-cases is:
- securing network traffic when using public WiFi at hotels, coffee shops or airports
- hiding illegally torrented content
- bypassing geo blocked content filters
- bypassing restrictive firewalls
- masking your IP address
Regardless of the specific use-case, a client must first establish a VPN tunnel between herself and the OpenVPN server. During connection establishment, the victim sends an initial UDP or TCP packet to a listening port on the OpenVPN server (typically 1194, but is configurable and known by the client). The subsequent packets exchanged establish a secure channel using SSL/TLS and various configuration parameters, including the client’s virtual IP address, are pushed to the client.
(C)lient OpenVPN (S)erver
|-- {src=C:Cport, dst=S:1194} ->| 1. First packet Client sends to OpenVPN server
|<- { Exchange parameters over SSL/TLS } ->| 2. Server and Client establish tunnel parameters
Fig. 1. Diagram showing the first packet the client sends to the server and the subsequent packets exchanged to establish the client’s tunnel parameters.
Once the client has a virtual-IP address that the OpenVPN server associates with her and her routes are configured to send all originating packets through the tunnel, the client may exchange packets between other globally routable IP addresses and they will assume the traffic originated from the OpenVPN server instead of the client. Also, any in-path machines between the OpenVPN client and server will only see encrypted traffic.
|----------| |------------------|
| Client A |~~~~~| In-path Machine |
|----------| |------------------|
| |------------|
. ~~~~~~~~~~~~| Web Server |
| |------------|
|----------| |----------------|
| Client B |~~~~~| OpenVPN Server |
|----------| |----------------|
Fig. 2. An example Topology of an OpenVPN Server and two VPN clients. There is also an in-path router between Client A and the OpenVPN server. If Client A (or B or Both) request data from Web Server, Web Server will think the requests came from OpenVPN server, regardless of which client issued the request. In-path Machine will not know if Client A requests a page from Web Server.
A recently disclosed attack by Tolley et al. Tolley2021 demonstrates how an attacker can leak connection information or even hijack connections between an OpenVPN client and an end-server. Their attacks require that the attacker be in-path between the OpenVPN client and the OpenVPN server. While in-path capabilities are typically reserved for nation-states, ISP, or similarly powerful adversaries, using network alchemy, we are able to deanonymize an OpenVPN client or transmute a basic OpenVPN client into an in-path attacker.
We define network alchemy as the creation of states of network connections that should not exist, using a combination of TTL limiting, packet spoofing, RFC ambiguities, and other low-level network primitives.
Conntrack stores state about connections it sees in a single, global
hash table called
nf_conntrack_hash
.
struct hlist_nulls_head *nf_conntrack_hash __read_mostly;
EXPORT_SYMBOL_GPL(nf_conntrack_hash);
Fig. 3. nf_conntrack_hash
table declaration.
The table is shared by all protocols and packets, regardless of
network namespaces or sockets. The table is initialized at module load
time and has a maximum size that is a function of the amount of system
memory. The size is also partially controlled by the system variable
net.ipv4.netfilter.nf_conntrack_max
. The fact that the table is
global and does not coordinate with the socket infrastructure are
the primary factors contributing to port-shadow behavior.
Individual connections are represented as entries in nf_conntrack_hash
by the
nf_conn
struct.
struct nf_conn {
.
.
.
u32 timeout;
.
.
.
/* XXX should I move this to the tail ? - Y.K */
/* These are my tuples; original and reply */
struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
/* Have we seen traffic both ways yet? (bitset) */
unsigned long status;
.
.
.
Fig. 4. nf_conn
struct including the fields related to this disclosure.
The metadata inside each nf_conn
entry includes a timeout
value, a
two-entry array of nf_conntrack_tuple_hash
structs (one for the
ORIGINAL
direction and one for the REPLY
direction), and a status
bitfield. Each nf_conntrack_tuple_hash
contains an
nf_conntrack_tuple
and each nf_conntrack_tuple
contains its own
copy of the packet’s direction, source and destination ports and IP
addresses, and protocol number.
/* Connections have two entries in the hash table: one for each way */
struct nf_conntrack_tuple_hash {
struct hlist_nulls_node hnnode;
struct nf_conntrack_tuple tuple;
};
Fig. 5. nf_conntrack_tuple_hash
declaration.
/* The manipulable part of the tuple. */
struct nf_conntrack_man {
union nf_inet_addr u3;
union nf_conntrack_man_proto u;
/* Layer 3 protocol */
u_int16_t l3num;
};
/* This contains the information to distinguish a connection. */
struct nf_conntrack_tuple {
struct nf_conntrack_man src;
/* These are the parts of the tuple which are fixed. */
struct {
union nf_inet_addr u3;
union {
/* Add other protocols here. */
__be16 all;
struct {
__be16 port;
} tcp;
struct {
__be16 port;
} udp;
struct {
u_int8_t type, code;
} icmp;
struct {
__be16 port;
} dccp;
struct {
__be16 port;
} sctp;
struct {
__be16 key;
} gre;
} u;
/* The protocol. */
u_int8_t protonum;
/* The direction (for tuplehash) */
u_int8_t dir;
} dst;
};
Fig. 6. nf_conntrack_tuple
and nf_conntrack_man
declarations. Notice that only
source portion of the tuple is manipulatable.
When Conntrack processes a packet, it checks to see whether it is in
nf_conntrack_hash
. If the packet is part of an existing connection,
Conntrack pulls the associated nf_conn
entry for the remainder of
processing, otherwise, a new nf_conn
entry is created and inserted
into nf_conntrack_hash
. If the nf_conn
entry is new, processing
includes remapping colliding ports and is performed by
get_unique_tuple
in Netfilter’s NAT module. As we demonstrate in the Attack
Methodology section, Netfitler’s port remapping (and, in special cases, lack thereof) make our
attacks possible (port shadowing leverages this
behavior). As the packet passes through Netfitler’s NAT hook, its source IP address
and port may be overwritten, depending on whether or not ports
collide.
The direction
field represents whether the packet is from the
connection originator or replier (i.e., ORIGINAL
and REPLY
). Conntrack
infers the connection’s direction based on whether an nf_conn
entry exists
for the given source and destination IP addresses, protocol number, and port
numbers in the case of UDP and TCP. When Conntrack sees a packet for which no
nf_conn
entry exists, it assumes that packet is the originator of the
connection and assigns nf_conn.tuplehash[ORIGINAL]
using the fields of the
packet being processed. This idea of connection direction is based on the
notion of “start of session”, and Conntrack’s use of ORIGINAL
and REPLY
directions are consistent with section 2.5. of
rfc2663.
2.5. Start of session for TCP, UDP and others
The first packet of every TCP session tries to establish a session and contains connection startup information. The first packet of a TCP session may be recognized by the presence of SYN bit and absence of ACK bit in the TCP flags. All TCP packets, with the exception of the first packet, must have the ACK bit set.
However, there is no deterministic way of recognizing the start of a UDP based session or any non-TCP session. A heuristic approach would be to assume the first packet with hitherto non-existent session parameters (as defined in section 2.3) as constituting the start of new session.
As stated previously, Conntrack stores two copies of the source and destination
IP addresses and ports, the protocol number, and the direction in each
nf_conn
entry in the tuplehash
array. In the case where the machine running
Conntrack is also the actual sender of packets in a session, the
ORIGINAL
direction’s source IP address is the machine running
Conntrack, the source port is whatever Conntrack’s sending
application chose, the destination IP address is some remote host’s IP
address, and the destination port is whatever port the remote host is using to communicate
with the Conntrack host. In the REPLY
direction, the source IP and
destination IP are switched and the destination is the Conntrack
machine’s IP address (i.e., the source in the ORIGINAL
direction). This
detail is important because with OpenVPN, from Conntrack’s
perspective, the ORIGINAL
direction for tunnel initializations should always
the OpenVPN client’s IP address. According to Netfilter, maintaining two entries
is an optimization for faster lookups and translations. Fig. 7 below provides a
generic example of what the nf_conntrack_hash
and some nf_conn
entry might
look like.
nf_conntrack_hash {
H .-> nf_conn[.] -> {
timeout = T;
tuplehash = {
[ORIGINAL] .-> {src={u3=Conntrack Machine, udp.port=sport}, dst={u3=Remote Server, udp.port=dport}},
[REPLY] .-> {src={u3=Remote Server, udp.port=dport}, dst={u3=Conntrack Machine, udp.port=sport}}
status = S;
};
}
}
Fig. 7. nf_conn
entry structure for entries in the
nf_conntrack_hash
table where the NAT is the actually end-point in a
connection session.
The following case the machine running Conntrack acts as a NAT. When a
host behind the NAT (with a private IP) sends a packet out the NAT,
the nf_conntrack_tuple[ORIGINAL]
direction’s source IP address is
the private IP address of the client sending the packet while the
source of the REPLY
direction is the NAT’s public IP
address. Assuming the port of this packet does not collide with other ports NAT is
translating, port numbers are swapped without being manipulated. This behavior is related to the c2mitm attack’s use of
the port shadowing primitive. Fig. 7 provides a graphical representation
of this situation.
nf_conntrack_hash {
H .-> nf_conn[.] -> {
timeout = T;
tuplehash = {
[ORIGINAL] .-> {src={u3=Machine Behind NAT, udp.port=sport}, dst={u3=Remote Server, udp.port=dport}},
[REPLY] .-> {src={u3=Remote Server, udp.port=dport}, dst={u3=Conntrack Machine, udp.port=sport}}
};
status = S;
}
}
Fig. 7. nf_conn
entry structure for NAT entries in the
nf_conntrack_hash
table.
If there are port collisions, then NAT will remap the ports, as depicted in the following figure.
nf_conntrack_hash {
H1 .-> nf_conn[.] -> {
timeout = T;
tuplehash = {
[ORIGINAL] .-> {src={u3=Remote Server, udp.port=Rport}, dst={u3=Conntrack Machine, udp.port=1194}},
[REPLY] .-> {src={u3=Conntrack Machine, udp.port=1194}, dst={u3=Remote Server, udp.port=Rport}}
};
status = S;
}
H2 .-> nf_conn[.] -> {
timeout = T;
tuplehash = {
[ORIGINAL] .-> {src={u3=Machine Behind NAT, udp.port=1194}, dst={u3=Remote Server, udp.port=Rport}},
[REPLY] .-> {src={u3=Remote Server, udp.port=NAT(Rport)}, dst={u3=Conntrack Machine, udp.port=1194}}
};
status = S;
}
}
Fig. 8. Depicts nf_conn
structure for NAT entries in the
nf_conntrack_hash
table when there is a port collision.
Fig. 8., assume that H1
was in the table before H2
. Notice that in the
REPLY
direction of the H2
entry that src.udp.port
is a modified version
of Rport, denoted by a function NAT
that remaps that port. This is a
simplified example of what happens when Netfilter’s NAT module resolves ports
that collide in this way. The collision happens because H1
in the ORIGINAL
direction and H2 in the REPLY
direction would hash to the same entry and
hence generate a non-unique tuple which cannot happen if NATing needs to
function correctly. We found that a malicious machine connected to the OpenVPN
server can shadow the OpenVPN server’s listening port (typically 1194) by
sending packets with the same source port as the OpenVPN server’s listening port to
arbitrary destination IP address and ports. Because these entries
are consulted for NAT before most packet processing and because they are used
to reroute packets, any packets matching an entry of this type bypasses any
listening sockets (such as the one OpenVPN uses) it was intended for. We will show how an attacker
can build a port shadow to deanonymize another OpenVPN client
or perform a c2mitm attack against a victim in the Attack Methodology section.
One challenge the attacker faces is uncertainty about when a victim will connect to the OpenVPN server.
nf_conn
entries are kept in the table until their timeout
value
expires. Conntrack’s garbage collector runs periodically and evicts
entries whose timeout has expired or if the table is filled beyond
95\%. In the latter case, Conntrack can evict entries before their
timeout has expired, but only if the entry’s status
does not have
the ASSURED
bit set. As packets are exchanged and as Netfilter
processes them, the timeout value and status get updated. An attacker
can take advantage of this to place entries in the ASSURED
state,
and periodically send packets through Conntrack to ensure the timeout
value never expires which is necessary for the c2mitm attack.
Placing an entry in the ASSURED
state is protocol dependent and
Conntrack requires that each protocol module maintain its state
according to that protocol’s requirements. There is no RFC to
dictate how this should or must be done, so the module authors make a
best effort to determine when the status
bits should be updated. In
the case of UDP, the ASSURED
bit is set whenever packets are
exchanged in both directions.
/* Returns verdict for packet, and may modify conntracktype */
int nf_conntrack_udp_packet(struct nf_conn *ct,
struct sk_buff *skb,
unsigned int dataoff,
enum ip_conntrack_info ctinfo,
const struct nf_hook_state *state)
{
.
.
.
/* If we've seen traffic both ways, this is some kind of UDP
* stream. Set Assured.
*/
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
.
.
.
/* Also, more likely to be important, and not a probe */
if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
nf_conntrack_event_cache(IPCT_ASSURED, ct);
.
.
.
return NF_ACCEPT;
}
Fig. 9. ASSURED bit handling for UDP packets in Conntrack.
For TCP, Conntrack maintains a shadow TCP state machine of a TCP
connection between two hosts whose packet’s the machine running
Conntrack routes. This is accomplished using the
tcp_conntrack
state transition table, a function to determine whether the incoming
packet should be routed based on Conntrack policy
(nf_conntrack_tcp_packet
), and the nf_conn.status
field. The
nf_conntrack_tcp_packet
function contains a code-path that sets the ASSURED
bit to 1.
/* Returns verdict for packet, or -1 for invalid. */
int nf_conntrack_tcp_packet(struct nf_conn *ct,
struct sk_buff *skb,
unsigned int dataoff,
enum ip_conntrack_info ctinfo,
const struct nf_hook_state *state)
{
.
.
.
} else if (!test_bit(IPS_ASSURED_BIT, &ct->status)
&& (old_state == TCP_CONNTRACK_SYN_RECV
|| old_state == TCP_CONNTRACK_ESTABLISHED)
&& new_state == TCP_CONNTRACK_ESTABLISHED) {
/* Set ASSURED if we see valid ack in ESTABLISHED
after SYN_RECV or a valid answer for a picked up
connection. */
set_bit(IPS_ASSURED_BIT, &ct->status);
nf_conntrack_event_cache(IPCT_ASSURED, ct);
}
.
.
.
return NF_ACCEPT;
}
Fig. 10. ASSURED bit handling for UDP packets in Conntrack.
Using this information, an attacker can keep her entries in the Conntrack table long enough to perform the c2mitm attack.
In addition to modifying the status
bits of the nf_conn
entry, Netfilter’s
TCP state machine makes forwarding decisions based on the TCP header and
Netfilter’s view of the TCP state between the two TCP end-points. This has
implications for which TCP packets can cross the NAT-external/ NAT-internal
address realms. TCP simultaneous open in particular causes security issues for
OpenVPN clients because an attacker can leverage Netfilter’s behavior when
processing SYNs to trick the victim into connecting to some TCP service through
the VPN server, back to the client. In the Attack Methodology section, we
demonstrate how an attacker can combine c2mitm and DNS injection with
simultaneous open to strip the OpenVPN’s tunnel encryption and establish a TCP
session with the victim.
In summary, we have described Netfilter’s stateful Connection tracking and network address translation implementation (Conntrack and NAT) as they relate to port shadowing. We described how entries are created, stored, used and destroyed by Conntrack. Next, we provide a description of how the attacker builds the port shadowing exploit primitive.
Port Shadowing Exploit Primitive
We identified an exploit primitive, port shadowing, that an attacker can use to either deanonymize a client connected to an OpenVPN server or perform a privilege escalation from client to man-in-the-middle. The attacks are related to each other and differ mainly in the context of their use of the exploit primitive as well as whether a client connects to the OpenVPN server before or after the attacker.
The attacker builds the exploit primitive in two steps. The process is the same for both attacks.
- Connect to the OpenVPN server.
- Send packets to a victim’s candidate, public IP address using the OpenVPN server’s listening port as the source port to all destination ports in the IANA ephemeral port range.
After step (2.) the OpenVPN server’s nf_conntrack_hash
contains
16383 entries. Each entry is capable of routing packets matching the
tuples in nf_conntrack_hash
back to the attacker’s OpenVPN tunnel
interface. We describe in the Attack Methodology section how an
attacker uses this to deanonymize the victim or become a man-in-the-middle
between the victim and the OpenVPN server.
We now describe our testing environment, the deanonymization and c2mitm attacks, and a third attack that demonstrates that network alchemy can be applied to TCP in addition to UDP connections.
We tested the attacks in a virtual environment and on the real Internet. Both environments ran OpenVPN server version 2.4.4 on an Ubuntu 18.04 headless server running Linux kernel version 4.15.0-142. We packaged our virtual environment as a vagrant script and include the required software dependencies and code to reproduce our attack (to the best of our abilities though some assembly may be required). Our live attack was performed against a server we setup and configured ourselves. We deployed our OpenVPN server on a machine with 2048 MB RAM, 1 core, and 55 GB storage using vultr cloud services. We manually installed OpenVPN 2.4.4 on the Ubuntu 18.04 vultr instance using Digital Oceans instructions. Our use of our own server for testing was to eliminate any interference our attacks may create with production clients using commercial or consumer VPN providers.
OpenVPN Client Deanonymization
Assumptions
The deanonymization attack makes the following assumption:
- The Victim (V) connects to the OpenVPN server (S) before the Attacker (A) carries out any attempt to see if they are connected. This is somewhat obvious, but we specify it as an assumption to make the exact nature of the attack clear. In this example, we assume the VPN uses UDP, however, these attacks are also possible using TCP.
nf_conntrack_hash {
H1 .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src={u3=V, udp.port=Vport}, dst={u3=S, udp.port=1194}},
[REPLY] .-> {src={u3=S, udp.port=1194}, dst={u3=V, udp.port=Vport}}
};
status = {ASSURED};
},
}
Fig. 11. S’s Conntrack table with V’s legitimate tunnel entry.
- A has two abilities: their own credentials to connect to the same OpenVPN server as V, and the ability to spoof packets on the Internet. A may or may not use two different computers for the attack.
Execution
The deanonymization attack is executed in four steps as follows:
-
A connects to S
nf_conntrack_hash { H1 .-> nf_conn[.] -> { timeout = 179; // seconds tuplehash = { [ORIGINAL] .-> {src={u3=V, udp.port=Vport}, dst={u3=S, udp.port=1194}}, [REPLY] .-> {src={u3=S, udp.port=1194}, dst={u3=V, udp.port=Vport}} }; status = {ASSURED}; }, H2 .-> nf_conn[.] -> { timeout = 179; // seconds tuplehash = { [ORIGINAL] .-> {src={u3=A, udp.port=Aport}, dst={u3=S, udp.port=1194}}, [REPLY] .-> {src={u3=S, udp.port=1194}, dst={u3=A, udp.port=Aport}} }; status = {ASSURED}; }, }
Fig. 12. S’s Conntrack table with V and A’s legitimate tunnel entries.
-
A sends UDP packets from herself to a candidate IP address of V. Each packet has the source port equal to whatever the OpenVPN server’s listening port is (assume 1194), a unique destination port in the IANA ephemeral port range, and is TTL limited such that the UDP packets do not reach the candidate IP.
The attacker must cycle through the victim’s ephemeral port space only once. This requires 16383 packets per candidate victim IP (assuming the operating system uses the IANA dynamic port range, some operating systems the victim may be using have a larger range). The attacker can check candidate victim IPs in parallel and send at any reasonable speed, and note also that the attacker does not need to check every one of the roughly 4 billion possible IPv4 addresses if they have a profile of the victim, such as what city they connect to the VPN from. We spoof responses in subsequent steps, which imposes a 180 second timeout (for the conntrack entry to be garbage collected) before we can check again if this particular candidate victim IP has connected.
nf_conntrack_hash { H1 .-> nf_conn[.] -> { timeout = 179; // seconds tuplehash = { [ORIGINAL] .-> {src={u3=V, udp.port=Vport}, dst={u3=S, udp.port=1194}}, [REPLY] .-> {src={u3=S, udp.port=1194}, dst={u3=V, udp.port=Vport}} }; status = {ASSURED}; }, H2 .-> nf_conn[.] -> { timeout = 179; // seconds tuplehash = { [ORIGINAL] .-> {src={u3=A, udp.port=Aport}, dst={u3=S, udp.port=1194}}, [REPLY] .-> {src={u3=S, udp.port=1194}, dst={u3=A, udp.port=Aport}} }; status = {ASSURED}; }, . . . Hn .-> nf_conn[.] -> { timeout = 179; // seconds tuplehash = { [ORIGINAL] .-> {src={u3=tun0A, port=1194}, dst={u3=V, udp.port=Vport}}, [REPLY] .-> {src={u3=V, port=NAT(Vport)}, dst={u3=S, udp.port=1194}} }; status = {ASSURED}; }, }
Fig. 13. S’s Conntrack table with V and A’s legitimate tunnel entry and A’s entry whose destination port in the
ORIGINAL
direction collides with V’s legitimate Conntrack entry’s source port in the Conntrack table. The entries are denoted by entries H1, H2, and Hn, respectively. -
From a seperate machine, A spoofs UDP responses for each of the UDP packets sent in (2.) from the candidate IP to S.
-
A’s machine connected to S collects the responses sent in (3.) routed back to her by S.
If A receives responses for every ephemeral port sent out in step
(2.), then she knows the candidate IP is not connected to the same
OpenVPN server (S) as her. If A observes one missing ephemeral
port response, then she knows the candidate IP is V and is connected to
the same OpenVPN server as her (she can repeat missing probes as needed to account for packet loss). This is because if V is connected to
the same OpenVPN server (S) as her, then S’s Conntrack table will have
an nf_conn
entry with S’s IP and OpenVPN port (1194) and V’s public
IP address and ephemeral port.
This entry makes it impossible for packets sent from A:1194 to V:Vport to
be differentiated from the original tuple. Conntrack/NAT must select
a new destination port for the packet sent in step (2.) to maintain
transparency and operational requirements specified in the RFCs.
OpenVPN Client-to-Man-in-the-Middle (c2mitm)
Assumptions
The c2mitm attack makes the following assumptions:
-
The attacker (A) knows the candidate, public IP address for a potential Victim (V).
-
V does not connect to the OpenVPN server (S) until after (A) builds the exploit primitive.
Assumption (1.) is viable in two situations. The first is if A has performed
the deanonymization attack in the past, and enumerated one or more victims that
includes V. The second is if V is at the same coffee shop or hotel as A. The
hotel or coffee shop is likely running its own NAT to share internet resources
with all the customers using the network. A can easily discover the public IP
address of the establishment. We imagine (2.) holding because, as stated above,
A can hold nf_conn
entries in the OpenVPN server’s table indefinitely.
Execution
The c2mitm attack is executed in three stages:
-
A connects to S
time | (A)ttacker OpenVPN (S)erver (V)ictim | |---{src=A:Aport, dst=S:1194}--->| 1.1. | |<--{src=S:1194, dst=A:Aport}----| 1.2. | tun0 |=============TUNNEL=============| 1.3. V
Fig 14. High-level space-time network diagram of A establishing OpenVPN tunnel to S.
nf_conntrack_hash { H1 .-> nf_conn[.] -> { timeout = 179; // seconds tuplehash = { [ORIGINAL] .-> {src={u3=A, udp.port=Aport}, dst={u3=S, udp.port=1194}}, [REPLY] .-> {src={u3=S, udp.port=1194}, dst={u3=A, udp.port=Aport}} }; status = {ASSURED}; } }
Fig 15. S’s
nf_conntrack_hash
table after A establishes her VPN tunnel. -
A sends UDP packets to a victim’s public IP address with S’s OpenVPN server listening port as A’s source port, creating a Conntrack entry that S uses for routing packets back to A with. A sends a packet for each ephemeral port of V.
The c2mitm attack requires that the attacker constant cycle through the ephemeral space to keep entries fresh until the victim attempts to connect to the VPN server. The conntrack entries may expire, so the attacker can either spoof replies to the VPN server with matching UDP parameters or refresh at a faster rate. Spoofing will place the entries in the ASSURED state where they cannot be evicted before 180 seconds, but adds the requirement that the attacker has the ability to spoof packets on the Internet and doubles the number of packets per port for the initial cycle. Since spoofing is already a requirement for the c2mitm attack it makes sense to do so, and somewhat reduces the necessary rate (about 182 packets per second, assuming the IANA dynamic port range). The attacker may also keep entries alive by cycling through the ephemeral port space every 30 seconds with a packet for each port (546 packets/second) and not spoof in this step, if they so desire. If the victim’s OS complies with rfc6056, section 3.2, then this is the worst case and the packet rate to keep conntrack entries fresh for all possible victim ephemeral ports is only about 717 packets per second. Remember that these are empty packets, half of which are encrypted and they are split across tens of thousands of flows.
time | (A)ttacker OpenVPN (S)erver (V)ictim | |-------------------{src=A:Aport, dst=S:1194}------------------->| | 1.1. | |<------------------{src=S:1194, dst=A:Aport}--------------------| | 1.2. | tun0 |===========================TUNNEL===============================| | 1.3. | |---{src=A:Aport, dst=S:1194, <src=tun0A:1194, dst=V:Vport1>}--->| | 2.1. | | |---{src=S:1194, dst=V:Vport1}-X | 2.2. : : : : | |---{src=A:Aport, dst=S:1194, <src=tun0A:1194, dst=V:VportN>}--->| | 2.3. | | |---{src=S:1194, dst=V:VportN}-X | 2.4. V
Fig 16. Space-time digram depicting execution step (2.) for c2mitm.
nf_conntrack_hash { H1 .-> nf_conn[.] -> { timeout = 179; // seconds tuplehash = { [ORIGINAL] .-> {src={u3=A, udp.port=Aport}, dst={u3=S, udp.port=1194}}, [REPLY] .-> {src={u3=S, udp.port=1194}, dst={u3=A, udp.port=Aport}} }; status = {ASSURED}; }, H2 .-> nf_conn[.] -> { timeout = 29; // seconds tuplehash = { [ORIGINAL] .-> {src={u3=tun0A, udp.port=1194}, dst={u3=V, udp.port=Vport1}}, [REPLY] .-> {src={u3=V, udp.port=Vport1}, dst={u3=S, udp.port=1194}} }; status = {UNREPLIED}; } : : Hk .-> nf_conn[.] -> { timeout = 29; // seconds tuplehash = { [ORIGINAL] .-> {src={u3=tun0A, udp.port=1194}, dst={u3=V, udp.port=VportN}}, [REPLY] .-> {src={u3=V, udp.port=VportN}, dst={u3=S, udp.port=1194}} }; status = {UNREPLIED}; } }
Fig 17. S’s Conntrack table after executing step (2.) for c2mitm.
-
V sends an OpenVPN connection request to S.
time
| (A)ttacker OpenVPN (S)erver (V)ictim
| |-------------------{src=A:Aport, dst=S:1194}------------------>| | 1.1.
| |<------------------{src=S:1194, dst=A:Aport}-------------------| | 1.2.
| tun0 |===========================TUNNEL==============================| | 1.3.
| |---{src=A:Aport, dst=S:1194, <src=tun0A:1194, dst=V:Vport>}--->| | 2.1.
| | |---{src=S:1194, dst=V:Vport}-x | 2.2.
| | |<--{src=V:Vport, dst=S:1194}---| 3.1.
| |<--{src=V:1194, dst=A:Aport, <src=V:Vport, dst=tun0A:1194>}----| | 3.2.
| |-------------------{src=A:Vport, dst=S:1194}------------------>| | 3.3.
| |<------------------{src=S:1194, dst=A:Vport}-------------------| | 3.4.
| |---{src=S:1194, dst=A:Vport, <src=tun0A:1194, dst=V:Vport>}--->| | 3.5.
| | |---{src=S:1194, dst=V:Vport}-->| 3.6.
| | |<--{src=V:Vport, dst=S:1194}---| 3.7.
| |<--{src=V:1194, dst=A:Aport, <src=V:Vport, dst=tun0A:1194>}----| | 3.8.
| |---{src=S:1194, dst=A:Vport, <src=tun0A:1194, dst=V:Vport>}--->| | 3.9.
| | |---{src=S:1194, dst=V:Vport}-->| 3.10.
V
Fig. 18. Network space-time diagram of the c2mitm attack to completion.
nf_conntrack_hash {
H1 .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src={u3=A, udp.port=Aport}, dst={u3=S, udp.port=1194}},
[REPLY] .-> {src={u3=S, udp.port=1194}, dst={u3=A, udp.port=Aport}}
};
status = {ASSURED};
},
H2 .-> nf_conn[.] -> {
timeout = 29; // seconds
tuplehash = {
[ORIGINAL] .-> {src=u3{=tun0A, udp.port=1194}, dst={u3=V, udp.port=Vport}},
[REPLY] .-> {src={u3=V, udp.port=Vport}, dst={u3=S, udp.port=1194}} ******
};
status = {ASSURED};
}
}
Fig. 19. Shows the server, S’s Conntrack table just after V sends a connection request to S and before A has relayed V’s connection request. This is at steps 3.1 and 3.2 of the Fig. 19 above. The asterisks indicate a state of network connection that should not exist, because V will attempt to initiate a connection but match a NAT rule as a REPLY.
nf_conntrack_hash {
H1 .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src={u3=A, udp.port=Aport}, dst={u3=S, udp.port=1194}},
[REPLY] .-> {src={u3=S, udp.port=1194}, dst={u3=A, udp.port=Aport}}
};
status = {ASSURED};
},
H2 .-> nf_conn[.] -> {
timeout = 29; // seconds
tuplehash = {
[ORIGINAL] .-> {src=u3{=tun0A, udp.port=1194}, dst={u3=V, udp.port=Vport}},
[REPLY] .-> {src={u3=V, udp.port=Vport}, dst={u3=S, udp.port=1194}}
};
status = {ASSURED};
},
H3 .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src=u{3=A, udp.port=Vport}, dst={u3=S, udp.port=1194}},
[REPLY] .-> {src={u3=S, udp.port=1194}, dst={u3=A, udp.port=Vport}}
};
status = {ASSURED};
},
}
Fig. 20. Shows S’s Conntrack table just after V sends a connection request to S and after A has relayed the V’s connection request, successfully become a man-in-the-middle between V and S (steps 3.4-3.10).
As we have shown, both the deanonymization and c2mitm attacks are possible because of implementation details in Conntrack’s NATing functionality. The difference in attacks is dependent on the order in which a victim connects to the same OpenVPN server as the attacker. Each case is harmful in its own right. The deanonymization attack is harmful because an often cited use case for VPN software is anonymity. The c2mitm attack places the attacker in-path for the victim’s connection to the VPN server, which could lead to DNS or TCP hijacking, traffic analysis, and other attacks that normally would be outside the reach of an off-path attacker who is just another VPN client.
Not only is the victim deanonymized, but the attacker is also positioned in the network in-path. From this position, the attacker can leverage a recently disclosed attack (Tolley2021 ) against OpenVPN servers. This attack assumes an in-path attacker. The attacker spoofs packet to the OpenVPN server from some other globally routable IP address such as a website or DNS server. The in-path attacker can use the DNS redirect primitive to send the victim to an attacker controlled server. If the server the victim was attempting to access is plaintext HTTP or the attacker has a compromised SSL/TLS certificate, the attacker has carte blanche over the victim.
Although the attacks we have described above enable the attacker to deanonymize the victim and redirect their web connections to an attacker-controlled server, demonstrating the c2mitm attack with TCP shows that the problem is not UDP specific and may open up other possible attacks that involve other services and ports.
Assumptions
- Attacker is in-path
- Attacker can spoof packets from a NAT-external DNS server to the OpenVPN server.
Execution
The attack is executed in two steps:
- Send TTL limited TCP SYN packets to the victim’s public IP address through the VPN.
time | (A)ttacker OpenVPN (S)erver (V)ictim | |----{src=A:80, dst=V:pA}-->|---{src=S:80, dst=V:pA}-x | 1.1. TCP SYN | |----{src=A:80, dst=V:pB}-->|---{src=S:80, dst=V:pB}-x | 1.2. TCP SYN . . . . | |----{src=A:80, dst=V:pN}-->|---{src=S:80, dst=V:pN}-x | 1.3. TCP SYN V
Fig. 21. Space-time diagram of A sending TTL limited SYN packets to V on all ephemeral ports.
nf_conntrack_hash { H1 .-> nf_conn[.] -> { timeout = 179; // seconds tuplehash = { [ORIGINAL] .-> {src={u3=A, tcp.port=80}, dst={u3=V, tcp.port=pA}}, [REPLY] .-> {src={u3=V, tcp.port=pA}, dst={u3=S, tcp.port=80}} }; protocol.tcp.state={SYN_SENT} status = {UNASSURED, UNREPLIED}; }, H2 .-> nf_conn[.] -> { timeout = 179; // seconds tuplehash = { [ORIGINAL] .-> {src={u3=A, tcp.port=80}, dst={u3=V, tcp.port=pB}}, [REPLY] .-> {src={u3=V, tcp.port=pB}, dst={u3=S, tcp.port=80}} }; protocol.tcp.state={SYN_SENT} status = {UNASSURED, UNREPLIED}; }, . . Hn .-> nf_conn[.] -> { timeout = 179; // seconds tuplehash = { [ORIGINAL] .-> {src={u3=A, tcp.port=80}, dst={u3=V, tcp.port=pN}}, [REPLY] .-> {src={u3=V, tcp.port=pN}, dst={u3=S, tcp.port=80}} }; protocol.tcp.state={SYN_SENT} status = {UNASSURED, UNREPLIED}; }, }
Fig. 21. S’s Conntrack table after all of A’s
N
SYN packets traverse Conntrack. - DNS inject the URL with the OpenVPN server’s IP address
- Victim initates 3-way handshake to OpenVPN server
time
| (A)ttacker OpenVPN (S)erver (V)ictim
| |<----{src=V:pA, dst=S:80, tcp.flags=SYN}----|<--{src=V:pA, dst=S:80, tcp.flags=SYN}-----| 1.1.
V
Fig. 22. Space-time diagram of V sending a SYN for as part of the TCP three-way handshake after a successful DNS injection by the attacker.
nf_conntrack_hash {
H1 .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src={u3=A, tcp.port=80}, dst={u3=V, tcp.port=pA}},
[REPLY] .-> {src={u3=V, tcp.port=pA}, dst={u3=S, tcp.port=80}}
};
protocol.tcp.state={SYN_SENT2}
status = {REPLIED};
},
H2 .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src={u3=A, tcp.port=80}, dst={u3=V, tcp.port=pB}},
[REPLY] .-> {src={u3=V, tcp.port=pB}, dst={u3=S, tcp.port=80}}
};
protocol.tcp.state={SYN_SENT}
status = {UNASSURED, UNREPLIED};
},
.
.
Hn .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src={u3=A, tcp.port=80}, dst={u3=V, tcp.port=pN}},
[REPLY] .-> {src={u3=V, tcp.port=pN}, dst={u3=S, tcp.port=80}}
};
protocol.tcp.state={SYN_SENT}
status = {UNASSURED, UNREPLIED};
},
}
Fig. 23. S’s Conntrack table after V’s SYN packet.
time
| (A)ttacker OpenVPN (S)erver (V)ictim
| |<----{src=V:pA, dst=S:80, tcp.flags=SYN}----|<--{src=V:pA, dst=S:80, tcp.flags=SYN}-----| 1.1.
| |---{src=V:pA, dst=S:80, tcp.flags=SYN/ACK}->|--{src=V:pA, dst=S:80, tcp.flags=SYN/ACK}->| 1.1.
V
Fig. 24. Space-time diagram of A sending a SYN/ACK as part of the TCP three-way handshake.
nf_conntrack_hash {
H1 .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src={u3=A, tcp.port=80}, dst={u3=V, tcp.port=pA}},
[REPLY] .-> {src={u3=V, tcp.port=pA}, dst={u3=S, tcp.port=80}}
};
protocol.tcp.state={SYN_RECV}
status = {REPLIED};
},
H2 .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src={u3=A, tcp.port=80}, dst={u3=V, tcp.port=pB}},
[REPLY] .-> {src={u3=V, tcp.port=pB}, dst={u3=S, tcp.port=80}}
};
protocol.tcp.state={SYN_SENT}
status = {UNASSURED, UNREPLIED};
},
.
.
Hn .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src={u3=A, tcp.port=80}, dst={u3=V, tcp.port=pN}},
[REPLY] .-> {src={u3=V, tcp.port=pN}, dst={u3=S, tcp.port=80}}
};
protocol.tcp.state={SYN_SENT}
status = {UNASSURED, UNREPLIED};
},
}
Fig. 25. S’s Conntrack table after A’s SYN/ACK packet.
time
| (A)ttacker OpenVPN (S)erver (V)ictim
| |<----{src=V:pA, dst=S:80, tcp.flags=SYN}----|<--{src=V:pA, dst=S:80, tcp.flags=SYN}-----| 1.1.
| |---{src=V:pA, dst=S:80, tcp.flags=SYN/ACK}->|--{src=V:pA, dst=S:80, tcp.flags=SYN/ACK}->| 1.1.
| |<----{src=V:pA, dst=S:80, tcp.flags=ACK}----|<--{src=V:pA, dst=S:80, tcp.flags=ACK}-----| 1.1.
V
Fig. 26. Space-time diagram of V sending the final ACK, completing the TCP three-way handshake.
nf_conntrack_hash {
H1 .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src={u3=A, tcp.port=80}, dst={u3=V, tcp.port=pA}},
[REPLY] .-> {src={u3=V, tcp.port=pA}, dst={u3=S, tcp.port=80}}
};
protocol.tcp.state={SYN_RECV}
status = {UNASSURED, REPLIED};
},
H2 .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src={u3=A, tcp.port=80}, dst={u3=V, tcp.port=pB}},
[REPLY] .-> {src={u3=V, tcp.port=pB}, dst={u3=S, tcp.port=80}}
};
protocol.tcp.state={SYN_SENT}
status = {UNASSURED, UNREPLIED};
},
.
.
Hn .-> nf_conn[.] -> {
timeout = 179; // seconds
tuplehash = {
[ORIGINAL] .-> {src={u3=A, tcp.port=80}, dst={u3=V, tcp.port=pN}},
[REPLY] .-> {src={u3=V, tcp.port=pN}, dst={u3=S, tcp.port=80}}
};
protocol.tcp.state={SYN_SENT}
status = {UNASSURED, UNREPLIED};
},
}
Fig. 27. S’s Conntrack table after V’s final ACK packet. Notice that the matching conntrack entry for the TCP connection (H1), is still in the UNASSURED and SYN_RECV state. This is because, from Netfitler’s perspective, A has not sent the final ACK to complete the simultaneous open. This also means the connection cannot be in the ASSURED state because that connection is technically not established from Netfilter’s perspective.
In addition to using the DNS redirect primitive to send the victim to some
NAT-external server, the attacker can even redirect the victim back to the
in-path machine through which the victim’s traffic are already being routed.
In this ouroboros configuration the attacker uses a similar methodology
for c2mitm, except this time, she sends TTL limited TCP SYN packets through the
VPN server to the victim with whatever port the client thinks the server is
using, e.g., port 80 or 443. Next, when the victim’s (forged) DNS query returns
the VPN server’s IP address, the victim sends a SYN packet to the VPN server.
Because the attacker primed the Conntrack table with SYN packets to each of the
victim’s ephemeral ports, the table is full of nf_conn
entries in the
SYN_SENT
state. When the victim’s SYN packet is translated using the matching
nf_conn
entry, that entry is updated to be in the SYN_SENT2
state and the
victim’s SYN packet is sent to the attacker. Next, the attacker sends an
SYN/ACK to the victim, who finally responds back to the attacker with the final
ACK. From the victim’s perspective, she is initiating a 3-way handshake, but
from the OpenVPN server (NAT)’s perspective, a simultaneous open is happening.
In this way, the attacker tricks the victim into connecting back to the in-path
router. Note that similar tricks can be used to connect to services running on a
victim behind a NAT or perform various port scans against such a victim.
Simultaneous open, described in rfc794, section 3.4, is a less
common connection establishment procedure where two machines initiate a connection between eachother by both exchanging SYNs, SYN/ACKs,
and ACKs similar to how the three-way handshake works.
There are two obvious ways to mitigate these issues, to some extent:
- The server could add firewall rules to prevent the port the VPN service is listening on from being used as a source port by clients.
- The NAT functionality could be changed (e.g., as the VPN server is sending decrypted packets over the virtual interface, or by changing the conntrack module of Netfilter upstream) so that any port not designated as “Dynamic and/or Private” by IANA is translated into such a port.
Either of these changes would prevent the specific attacks presented above. However, one can envision other attacks that do not involve specific ports, such as attacks on BitTorrent users.
A comprehensive fix to this vulnerability would entail somehow modifying the notions of garbage collection, connection direction, connection status, and simultaneous open in NAT implementations to be more consistent with the security and privacy requirements of VPNs.
NAT Security Considerations in RFCs
The RFCs mentioned throughout this disclosure make various references to security and privacy. However, the level of security provided by a NAT based on network configuration is not clearly defined. Furthermore, the NAT-router + server situation, as in the OpenVPN case, gives rise to this particular attack. This host configuration is not explicitly addressed in the RFCs, though some do state that the NAT-external IPs and ports should be managed “appropriately” using firewall rules. The lack of end-to-end security is known and stated in the RFCs as is the fact that NAT brakes end-to-end model of IP and is simply a work-around to prolonging IPv4’s lifetime. The following are some specific examples of security considerations related to our attack.
rfc 2663, section 9, states:
Many people view traditional NAT router as a one-way (session) traffic filter, restricting sessions from external hosts into their machines.
However, our port shadowing examples, particularly our abuse of TCP simultaneous open, demonstrate that an attacker can bypass one-way traffic filtering in specification conditions.
Determine need for public toward private connections, variability of destinations on the private side, and potential for simultaneous use of public side port numbers. NAPTs increase administration if these apply.
Port shadowing demonstrates why this point is critical to preserving client security and privacy.
rfc 4787, section 13 states:
This document recommends that the NAT filters be specific to the external IP address only (see REQ-8) and not to the external IP address and UDP port. It can be argued that this is less secure than using the IP and port. Devices that wish to filter on IP and port do still comply with these requirements.
However, port shadowing clearly requires some port-dependent action be taken by the NAT to mitigate unintentionally forwarding packets to an attacker.
NAT related RFCs:
Protocol RFCs:
While this write-up covers Netfilter and Linux in detail, we have also tested our attacks on FreeBSD 13 using natd, ipfw, ipf, and pf. We found that all the implementations are susepctible to port shadowing, however, only natd appears to succeptable to a c2mitm using OpenVPN the same was as Netfilter on Linux.
The following CPE information is the software setup for our tests.
- part=”a”, vendor=”Netfilter”, product=”Netfilter”
- part=”o”, vendor=”Linux”, product=”Linux”, version=”4.15.0-142”
- part=”a”, vendor=”OpenVPN”, product=”OpenVPN Server”, version=”2.4.4”,
- part=”o”, vendor=”FreeBSD”, product=”FreeBSD-13”
- part=”a”, vendor=”FreeBSD”, product=”natd”
We would like to thank our colleagues, William J. Tolley, Beau Kujath, and Jeffrey Knockel for their input and feedback during the analysis and attack execution phases of this work.
This project received funding from the following sources:
This material is based upon work supported by the U.S. National Science Foundation under Grant Nos. 1801613 and 2007741 . Any opinions, findings and conclusions or recommendations expressed in this material do not necessarily reflect the views of the National Science Foundation.