TCP: Add support for TCP Stealth
Subsystems (3)
PATCH 1/1 TCP: Add support for TCP Stealth
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi,
one year ago [0] we tried to convince you to add support for a new socket option to the linux kernel. Equipped with an improved version of our patch we're back to accomplish this task today. :-)
TCP Stealth is a modern variant of port knocking which borrows techniques from network steganography to enable clients to authenticate themselves towards a server on TCP level. You can find technical details in an rfc draft we wrote earlier this year [1] and in my master's thesis [2]. In summary, TCP Stealth derives authentication information from a pre-shared secret and embeds it into the ISN sent along with the first SYN from the client.
Our motivation is simple: During this year we gained hard evidence on secret services actively port scanning the internets followed by exploitation of your services using 0-day exploits [3, 4]. We don't want our machines to be turned into relays from where they continue to cascade their attacks. TCP Stealth makes port scanning more expensive by a factor of 2^31 (on average).
A copy of this patch as well as patches for several user space applications can be found on the project's home page [5].
All the best for the upcoming year,
Julian & Christian
[0] https://lkml.org/lkml/2013/12/10/1155
[1] https://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/
[2] https://gnunet.org/kirsch2014knock
[3]
http://www.heise.de/ct/artikel/NSA-GCHQ-The-HACIENDA-Program-for-Internet-Colonization-2292681.html
[4]
https://firstlook.org/theintercept/2014/12/13/belgacom-hack-gchq-inside-story/
[5] https://gnunet.org/knock
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQEcBAEBAgAGBQJUpHCvAAoJENwkOWttRRA4g10IALbJZU9/5Gp8tVdpXqbkOIMp Kz+yOMyYULqYeM8yguSBZjZLbaz/VAS7SNpQxKGU+W0aAXa22FsSfVoUU7wqp3NT 3EGRuPkMaJkQ66IP8MtX+6/hSeWSh78tEaIFWVjyutihPyQGz0LefFc66gm54X4T s8IYW7jKFhNmmROu9CXLTxq4B5t2v+Evv/qWqotZqR1t3IbIUmZAiKrlkMRd7dtM SaS5JwFeiObxn+0M/7javQCAhfgPXYEOU0QKAGY55MXcPAner/5PuExIZdOJ41R3 XD9tgoLGhHEiQkxj0/bP2cs3Cl5xfJl9t2iecVfTIR7PytaTJ/kFuE4gNgWEcTA= =T6/C
-----END PGP SIGNATURE-----
include/linux/tcp.h +16 -0
@@ -19,6 +19,7 @@#include <linux/skbuff.h> +#include <linux/cryptohash.h> #include <net/sock.h> #include <net/inet_connection_sock.h> #include <net/inet_timewait_sock.h>
@@ -309,6 +310,21 @@ struct tcp_sock {struct tcp_md5sig_info __rcu *md5sig_info;#endif +#ifdef CONFIG_TCP_STEALTH +/* Stealth TCP socket configuration */ +struct { +#define TCP_STEALTH_MODE_AUTH BIT(0) +#define TCP_STEALTH_MODE_INTEGRITY BIT(1) +#define TCP_STEALTH_MODE_INTEGRITY_LEN BIT(2) +int mode; +u8 secret[MD5_MESSAGE_BYTES ]; +int integrity_len; +u16 integrity_hash; +struct skb_mstamp mstamp; +bool saw_tsval; + } stealth; +#endif +/* TCP fastopen related information */ struct tcp_fastopen_request *fastopen_req;/* fastopen_rsk points to request_sock that resulted in this big
include/net/secure_seq.h +5 -0
@@ -14,5 +14,10 @@ u64 secure_dccp_sequence_number(__be32 s __be16 sport, __be16 dport);u64 secure_dccpv6_sequence_number (__be32 *saddr,__be32 *daddr,__be16 sport,__be16 dport); +#ifdef CONFIG_TCP_STEALTH +u32 tcp_stealth_do_auth (struct sock *sk,struct sk_buff *skb); +u32 tcp_stealth_sequence_number (struct sock *sk,__be32 *daddr, +u32 daddr_size,__be16 dport); +#endif #endif /* _NET_SECURE_SEQ */
include/net/tcp.h +6 -0
@@ -439,6 +439,12 @@ void tcp_parse_options(const struct sk_bstruct tcp_options_received *opt_rx, int estab, struct tcp_fastopen_cookie *foc);const u8 *tcp_parse_md5sig_option (const struct tcphdr *th); +#ifdef CONFIG_TCP_STEALTH +const bool tcp_parse_tsval_option (u32 *tsval,const struct tcphdr *th); +int tcp_stealth_integrity (u16 *hash,u8 *secret,u8 *payload,int len); +#define be32_isn_to_be16_av (x) (((__be16 *)&x)[0]) +#define be32_isn_to_be16_ih (x) (((__be16 *)&x)[1]) +#endif /* * TCP v4 functions exported for the inet6 API
include/uapi/linux/tcp.h +3 -0
@@ -112,6 +112,9 @@ enum {#define TCP_FASTOPEN 23/* Enable FastOpen on listeners */ #define TCP_TIMESTAMP 24#define TCP_NOTSENT_LOWAT 25/* limit number of unsent bytes in write queue */ +#define TCP_STEALTH 26 +#define TCP_STEALTH_INTEGRITY 27 +#define TCP_STEALTH_INTEGRITY_LEN 28struct tcp_repair_opt {__u32 opt_code;
net/core/secure_seq.c +101 -0
114 lines not on this page · raw message
net/ipv4/Kconfig +10 -0
@@ -671,3 +671,13 @@ config TCP_MD5SIG on the Internet. If unsure, say N. + +config TCP_STEALTH + bool "TCP: Stealth TCP socket support" + default n + ---help--- + This option enables support for stealth TCP sockets. If you do not + know what this means, you do not need it. + + If unsure, say N. +
net/ipv4/tcp.c +110 -0
128 lines not on this page · raw message
net/ipv4/tcp_input.c +86 -0
116 lines not on this page · raw message
net/ipv4/tcp_ipv4.c +27 -0
@@ -75,6 +75,7 @@#include <net/secure_seq.h> #include <net/tcp_memcontrol.h> #include <net/busy_poll.h> +#include <net/secure_seq.h> #include <linux/inet.h> #include <linux/ipv6.h>
@@ -235,6 +236,21 @@ int tcp_v4_connect(struct sock *sk, stru sk->sk_gso_type =SKB_GSO_TCPV4 ;sk_setup_caps (sk, &rt->dst); +#ifdef CONFIG_TCP_STEALTH +/* If CONFIG_TCP_STEALTH is defined, we need to know the timestamp as +* early as possible and thus move taking the snapshot of tcp_time_stamp +* here. +*/ +skb_mstamp_get (&tp->stealth.mstamp); + +if (!tp->write_seq &&likely (!tp->repair) && +unlikely (tp->stealth.mode &TCP_STEALTH_MODE_AUTH )) + tp->write_seq =tcp_stealth_sequence_number (sk, + &inet->inet_daddr, +sizeof (inet->inet_daddr), + usin->sin_port); +#endif +if (!tp->write_seq &&likely (!tp->repair)) tp->write_seq = secure_tcp_sequence_number(inet->inet_saddr, inet->inet_daddr,
@@ -1423,6 +1439,8 @@ static struct sock *tcp_v4_hnd_req(struc*/ int tcp_v4_do_rcv (struct sock *sk,struct sk_buff *skb) { +struct tcp_sock *tp =tcp_sk (sk); +struct tcphdr *th =tcp_hdr (skb);struct sock *rsk;if (sk->sk_state ==TCP_ESTABLISHED ) {/* Fast path */
@@ -1443,6 +1461,15 @@ int tcp_v4_do_rcv(struct sock *sk, strucif (skb->len <tcp_hdrlen (skb) ||tcp_checksum_complete (skb)) goto csum_err; +#ifdef CONFIG_TCP_STEALTH +if (sk->sk_state ==TCP_LISTEN && th->syn && !th->fin && +unlikely (tp->stealth.mode &TCP_STEALTH_MODE_AUTH ) && +tcp_stealth_do_auth (sk, skb)) { + rsk = sk; + goto reset; + } +#endif +if (sk->sk_state ==TCP_LISTEN ) {struct sock *nsk =tcp_v4_hnd_req (sk, skb);if (!nsk)
net/ipv4/tcp_output.c +15 -0
@@ -915,6 +915,13 @@ static int tcp_transmit_skb(struct sock tcb =TCP_SKB_CB (skb);memset (&opts,0 ,sizeof (opts)); +#ifdef TCP_STEALTH +if (unlikely (tcb->tcp_flags &TCPHDR_SYN && + tp->stealth.mode &TCP_STEALTH_MODE_AUTH )) { + skb->skb_mstamp = tp->stealth.mstamp; + } +#endif +if (unlikely (tcb->tcp_flags &TCPHDR_SYN )) tcp_options_size =tcp_syn_options (sk, skb, &opts, &md5);else
@@ -3109,7 +3116,15 @@ int tcp_connect(struct sock *sk)skb_reserve (buff,MAX_TCP_HEADER );tcp_init_nondata_skb (buff, tp->write_seq++,TCPHDR_SYN ); +#ifdef CONFIG_TCP_STEALTH +/* The timetamp was already made at the time the ISN was generated +* as we need to know its value in the stealth_tcp_sequence_number() +* function. +*/ + tp->retrans_stamp = tp->stealth.mstamp.stamp_jiffies; +#else tp->retrans_stamp = tcp_time_stamp; +#endif tcp_connect_queue_skb (sk, buff);tcp_ecn_send_syn (sk, buff);
net/ipv6/tcp_ipv6.c +25 -1
@@ -63,6 +63,7 @@#include <net/secure_seq.h> #include <net/tcp_memcontrol.h> #include <net/busy_poll.h> +#include <net/secure_seq.h> #include <linux/proc_fs.h> #include <linux/seq_file.h>
@@ -297,6 +298,21 @@ static int tcp_v6_connect(struct sock *sip6_set_txhash (sk); +#ifdef CONFIG_TCP_STEALTH +/* If CONFIG_TCP_STEALTH is defined, we need to know the timestamp as +* early as possible and thus move taking the snapshot of tcp_time_stamp +* here. +*/ +skb_mstamp_get (&tp->stealth.mstamp); + +if (!tp->write_seq &&likely (!tp->repair) && +unlikely (tp->stealth.mode &TCP_STEALTH_MODE_AUTH )) + tp->write_seq =tcp_stealth_sequence_number (sk, + sk->sk_v6_daddr.s6_addr32, +sizeof (sk->sk_v6_daddr), + inet->inet_dport); +#endif +if (!tp->write_seq &&likely (!tp->repair)) tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32, sk->sk_v6_daddr.s6_addr32,
@@ -1251,7 +1267,8 @@ out:static int tcp_v6_do_rcv (struct sock *sk,struct sk_buff *skb) {struct ipv6_pinfo *np =inet6_sk (sk); -struct tcp_sock *tp; +struct tcp_sock *tp =tcp_sk (sk); +struct tcphdr *th =tcp_hdr (skb);struct sk_buff *opt_skb =NULL ;/* Imagine: socket is IPv6. IPv4 packet arrives,
@@ -1310,6 +1327,13 @@ static int tcp_v6_do_rcv(struct sock *skif (skb->len <tcp_hdrlen (skb) ||tcp_checksum_complete (skb)) goto csum_err; +#ifdef CONFIG_TCP_STEALTH +if (sk->sk_state ==TCP_LISTEN && th->syn && !th->fin && + tp->stealth.mode &TCP_STEALTH_MODE_AUTH && +tcp_stealth_do_auth (sk, skb)) + goto reset; +#endif +if (sk->sk_state ==TCP_LISTEN ) {struct sock *nsk =tcp_v6_hnd_req (sk, skb);if (!nsk)
Hi Julian,
On 12/31/2014 10:54 PM, Julian Kirsch wrote:
...
Quoted 11 lines
one year ago [0] we tried to convince you to add support for a new socket option to the linux kernel. Equipped with an improved version of our patch we're back to accomplish this task today. :-)
TCP Stealth is a modern variant of port knocking which borrows techniques from network steganography to enable clients to authenticate themselves towards a server on TCP level. You can find technical details in an rfc draft we wrote earlier this year [1] and in my master's thesis [2]. In summary, TCP Stealth derives authentication information from a pre-shared secret and embeds it into the ISN sent along with the first SYN from the client.
/me wondering (haven't tried that though) ... have you considered f.e. building a library using a raw packet socket with a BPF filter to capture SYN packets and then TCP_REPAIR [1] to build a full-blown TCP socket out of it in case of a correct authentication from the ISN?
Thanks,
Daniel
[1] http://www.criu.org/TCP_connection
Quoted 35 lines
Our motivation is simple: During this year we gained hard evidence on secret services actively port scanning the internets followed by exploitation of your services using 0-day exploits [3, 4]. We don't want our machines to be turned into relays from where they continue to cascade their attacks. TCP Stealth makes port scanning more expensive by a factor of 2^31 (on average).
A copy of this patch as well as patches for several user space applications can be found on the project's home page [5].
All the best for the upcoming year,
Julian & Christian[0] https://lkml.org/lkml/2013/12/10/1155
[1] https://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/
[2] https://gnunet.org/kirsch2014knock
[3]
http://www.heise.de/ct/artikel/NSA-GCHQ-The-HACIENDA-Program-for-Internet-Colonization-2292681.html
[4]
https://firstlook.org/theintercept/2014/12/13/belgacom-hack-gchq-inside-story/
[5] https://gnunet.org/knock
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1iQEcBAEBAgAGBQJUpHCvAAoJENwkOWttRRA4g10IALbJZU9/5Gp8tVdpXqbkOIMp Kz+yOMyYULqYeM8yguSBZjZLbaz/VAS7SNpQxKGU+W0aAXa22FsSfVoUU7wqp3NT 3EGRuPkMaJkQ66IP8MtX+6/hSeWSh78tEaIFWVjyutihPyQGz0LefFc66gm54X4T s8IYW7jKFhNmmROu9CXLTxq4B5t2v+Evv/qWqotZqR1t3IbIUmZAiKrlkMRd7dtM SaS5JwFeiObxn+0M/7javQCAhfgPXYEOU0QKAGY55MXcPAner/5PuExIZdOJ41R3 XD9tgoLGhHEiQkxj0/bP2cs3Cl5xfJl9t2iecVfTIR7PytaTJ/kFuE4gNgWEcTA= =T6/C
-----END PGP SIGNATURE-----
Dear Daniel,
That approach is highly vulnerable to timing attacks, and doesn't answer how TCP clients without special capabilities could set the ISN correctly either. Playing with raw sockets is the kind of geeky hack that is unlikely to give us the combination of usability and security required to significantly reduce the ongoing large-scale compromise of network equipment by spy agencies.
Christian
Quoted 8 lines · Daniel Borkmann
/me wondering (haven't tried that though) ... have you considered f.e. building a library using a raw packet socket with a BPF filter to capture SYN packets and then
TCP_REPAIR[1] to build a full-blown TCP socket out of it in case of a correct authentication from the ISN?Thanks,
Daniel
On 01/01/2015 04:32 PM, Christian Grothoff wrote:
...
Quoted 3 lines
That approach is highly vulnerable to timing attacks, and doesn't answer how TCP clients without special capabilities could set the ISN correctly either. Playing with raw sockets is the kind of geeky hack that is
Right, for client/server side you'd need to have the capabilities and then drop them later, which would make that approach less convenient for user space applications (despite not having to have a port knocking uapi in the TCP core itself). For the server, you might get away with a central daemon spawning sockets via IPC, but for the unprivileged client to e.g., let it set it's own ISN via setsockopt(2) would be a bad idea.
Quoted 3 lines
unlikely to give us the combination of usability and security required to significantly reduce the ongoing large-scale compromise of network equipment by spy agencies.
Out of curiosity, when you say you want to significantly reduce the large-scale compromise of services by hiding ports, how do you solve
i) the pre-shared key distribution issue you need for your approach (are you mostly targeting administrators for accessing their companies router/firewall management interfaces?), and ii) the broad adoption of this setsockopt(2) in applications? I think for ii) it would be great not having to change and recompile every possible client _and_ server application if they don't have the change upstreamed in their project. It feels like a property that goes beyond the scope of a specific, individual application, put differently, what about an additional central configuration interface? Other than that, is there a plan for key rotations in other words, to have a grace period for a key transition as peers might not have synced clocks?
Quoted 18 lines · Daniel Borkmann
On 01/01/2015 04:32 PM, Christian Grothoff wrote:
...Quoted 3 lines
That approach is highly vulnerable to timing attacks, and doesn't answer how TCP clients without special capabilities could set the ISN correctly either. Playing with raw sockets is the kind of geeky hack that is
Right, for client/server side you'd need to have the capabilities and then drop them later, which would make that approach less convenient for user space applications (despite not having to have a port knocking uapi in the TCP core itself). For the server, you might get away with a central daemon spawning sockets via IPC, but for the unprivileged client to e.g., let it set it's own ISN via setsockopt(2) would be a bad idea.
Quoted 3 lines
unlikely to give us the combination of usability and security required to significantly reduce the ongoing large-scale compromise of network equipment by spy agencies.
Quoted 5 lines
Out of curiosity, when you say you want to significantly reduce the large-scale compromise of services by hiding ports, how do you solve
i) the pre-shared key distribution issue you need for your approach (are you mostly targeting administrators for accessing their companies router/firewall management interfaces?),
Our assumption is that this solution will work if SSH access to the system is limited to a small set of users that can easily agree on an additional passphrase. We do not claim to solve the key distribution issue in general (the GNU Name System can solve it for some application scenarios, but that's out of scope for Knock). Like most security solutions, this works for some scenarios, but not all.
Quoted 4 lines
and ii) the broad adoption of
this setsockopt(2) in applications? I think for ii) it would be great not having to change and recompile every possible client _and_ server application if they don't have the change upstreamed in their project.
Correct, for that we have libknockify, a library you can LD_PRELOAD, setting the passphrase via an environment variable. Now, this doesn't work for applications that don't permit LD_PRELOAD (like openssh) or use some TCP sockets where TCP Stealth should be active, and others where it should not be. But for tools like 'netcat', 'telnet', 'wget' and many other small tools, libknockify will work.
Quoted 3 lines
It feels like a property that goes beyond the scope of a specific, individual application, put differently, what about an additional central configuration interface?
With our patch for systemd support, we kind of have a 'central' configuration interface for "next-generation" servers that support systemd-style listening. For clients, using libknockify works usually pretty well as clients usually don't have many different types of TCP sockets open.
Quoted 3 lines
Other than that, is there a plan for
key rotations in other words, to have a grace period for a key transition as peers might not have synced clocks?
I have no such plans, as it would complicate the system quite a bit and thus also reduces usability. It would way complicate the key distribution issue you mentioned, and also the libknockify-style integration where currently the PSK can simply be passed via an environment variable would also have to be done differently (i.e. contacting some kind of PSK-oracle service via IPC). The result would be a huge increase in complexity for IMO a rather small gain in security for possibly only a small set of users.
Quoted 4 lines · On Wed, 31 Dec 2014 22:54:59 +0100 Julian Kirsch <[email protected]> wrote:
include/linux/tcp.h +4
-0+#ifdef CONFIG_TCP_STEALTH +/* Stealth TCP socket configuration */ + struct { + #define TCP_STEALTH_MODE_AUTH BIT(0)
include/linux/tcp.h struct tcp_sock { · line 326
Quoted 10 lines
include/linux/tcp.h +10
-0+#define TCP_STEALTH_MODE_INTEGRITY BIT(1) +#define TCP_STEALTH_MODE_INTEGRITY_LEN BIT(2) +int mode; +u8 secret[MD5_MESSAGE_BYTES ]; +int integrity_len; +u16 integrity_hash; +struct skb_mstamp mstamp; + bool saw_tsval; + } stealth; +#endif
If you want a bitfield, why not use a bitfield for mode? If you have to use masks, better to use u8 for mode. Integrity length should be unsigned since obviously negative values are not possible.
Rearrange structure to save space. Lots of holes here.
net/core/secure_seq.c static u32 seq_scale(u32 seq) · line 71
Quoted 12 lines · On Wed, 31 Dec 2014 22:54:59 +0100 Julian Kirsch <[email protected]> wrote:
net/core/secure_seq.c +12
-0+memcpy (iv, (const __u8 *)daddr, + (daddr_size >sizeof (iv)) ?sizeof (iv) : daddr_size); + +#ifdef CONFIG_TCP_MD5SIG + md5 = tp->af_specific->md5_lookup (sk, sk); +#else + md5 =NULL ; +#endif +if (likely (sysctl_tcp_timestamps && !md5) || tp->stealth.saw_tsval) + tsval = tp->stealth.mstamp.stamp_jiffies; + + ((__be16 *)iv)[2 ] ^=cpu_to_be16 (tp->stealth.integrity_hash);
Cast unnecessary on memcpy arg since it takes void *
Would be clearer to use a real structure or union not assignment to cast to setup iv.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi Stephen,
thanks a lot for your input. I've reflected your suggestions in the
code: The mode member of the stealth struct is now a u8, integrity_len is now unsigned (size_t), I've switched the integrity_len and integrity_hash members in order to close the gaps in the struct (sorry for wasting bits) and I removed the cast from the second parameter of the memcopy you mentioned. While I totally agree with you that the part of the iv-setup where we xor the hash, tsval and dport in is not very readable, I'd argue that declaring and using a struct would make the patch longer while the semantics of such a struct really are needed only in context of a single function and therefore cannot be reused. Do you think that accessing the elements with a macro (see below) could be an alternative?
#define tcp_stealth_iv_integrity_hash(iv) (((__be16 *)&iv)[2])
Best,
Julian
Quoted 13 lines · Stephen Hemminger
Quoted 7 lines · On Wed, 31 Dec 2014 22:54:59 +0100 Julian Kirsch <[email protected]> wrote:
(quoted diff) +1
-0+ memcpy(iv, (const __u8 *)daddr, + (daddr_size >sizeof(iv)) ? sizeof(iv) :
daddr_size); + +#ifdefCONFIG_TCP_MD5SIG+ md5 =tp->af_specific->md5_lookup(sk, sk);
+#else + md5 = NULL; +#endif + if (likely(sysctl_tcp_timestamps
&& !md5) ||tp->stealth.saw_tsval) + tsval =tp->stealth.mstamp.stamp_jiffies; + + ((__be16*)iv)[2] ^=cpu_to_be16(tp->stealth.integrity_hash);Cast unnecessary on memcpy arg since it takes void *
Quoted 6 lines
Would be clearer to use a real structure or union not assignment to cast to setup iv. -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected] More majordomo info at
http://vger.kernel.org/majordomo-info.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQEcBAEBAgAGBQJUpdjhAAoJENwkOWttRRA4ZFYIALs7gskvZqlCzFCuNTsJ4js9 7x5OTsuyY5caOIEcveVqYnW2dcOO2Jtwe1QUIOsRo7X6YOEA/8IT6+sJ0fxViLTD TJAzU670Kcecn7+0cHPAj31yW+t9SHb5BBzMLCJlhSAboMs0YKmkwetqffg013uP x81OI6kJy6pUCAeBeyyy20QafrIhs5vjEILGf9qSzeoIXRBdpnuH99FzoxEjOkUA ka4QtrAUh3Uk0s6H8ezcpqvY2bKcz7te8+af5XF+Kz/+DLatoN2x58psxw3irBCw x18FRVsbgmXY/m3leKilK4ieCyO1LzafRNa674fSW6QtHHMDZwSoYi0kDjSNMRk= =ke3l
-----END PGP SIGNATURE-----
On 31 December 2014 at 22:54, Julian Kirsch <[email protected]> wrote:
Hey Jullian
Quoted 3 lines
one year ago [0] we tried to convince you to add support for a new socket option to the linux kernel. Equipped with an improved version of our patch we're back to accomplish this task today. :-)
Nice, what is the difference from the previous version? There is no changelog in the patch. Especially issues raised by Eric (cc'ed) one year ago and TCPM.
Hagen