Hi, I wrote a patch to program a very simple steganographic buffer into the pf firewalling system. However I'm running into a problem. It turns out at least to me, that pf's scrub gets called twice on output. Why is this?
I'm making my patch available and the program to program the buffer into pf. It's ugly but I don't see a need to commit this ever, it's a small personal project of mine... When you see the code you'll see that when ip_stegid() gets called it increments the buffer offset by two, I'm missing data. When I program the buffer with '11223344556677889900AABBCCDDEEFF', in a tcpdump I see this: 20:33:13.120728 192.168.65.2 > 192.168.179.1: icmp: echo request (id:7195 seq:7) [icmp cksum ok] (ttl 255, id 12593, len 84) 0000: 4500 0054 3131 0000 ff01 1523 c0a8 4102 E..T11.....#..A. 0010: c0a8 b301 0800 a4e7 7195 0007 2994 298a ........q...).). 20:33:14.120730 192.168.65.2 > 192.168.179.1: icmp: echo request (id:7195 seq:8) [icmp cksum ok] (ttl 255, id 13107, len 84) 0000: 4500 0054 3333 0000 ff01 1321 c0a8 4102 E..T33.....!..A. 0010: c0a8 b301 0800 7b05 7195 0008 2994 298a ......{.q...).). 20:33:15.120732 192.168.65.2 > 192.168.179.1: icmp: echo request (id:7195 seq:9) [icmp cksum ok] (ttl 255, id 13621, len 84) 0000: 4500 0054 3535 0000 ff01 111f c0a8 4102 E..T55........A. 0010: c0a8 b301 0800 c128 7195 0009 2994 298a .......(q...).). 20:33:16.120737 192.168.65.2 > 192.168.179.1: icmp: echo request (id:7195 seq:10) [icmp cksum ok] (ttl 255, id 14135, len 84) 0000: 4500 0054 3737 0000 ff01 0f1d c0a8 4102 E..T77........A. 0010: c0a8 b301 0800 9580 7195 000a 2994 298a ........q...).). 20:33:17.120736 192.168.65.2 > 192.168.179.1: icmp: echo request (id:7195 seq:11) [icmp cksum ok] (ttl 255, id 14649, len 84) 0000: 4500 0054 3939 0000 ff01 0d1b c0a8 4102 E..T99........A. 0010: c0a8 b301 0800 0a73 7195 000b 2994 298a .......sq...).). So 11, 33, 55, 77, 99... exactly every 2nd value gets skipped. For steganography this would suck as I'm trying to relay a message from one computer to another and if only half arrives and it can't reassemble it it's no good. I'm not saying it has to be like TCP but some reliability would be nice. I'm guessing it's because ip_stegid() gets called twice per packet that is outgoing. Here is my rule btw for this: beta# pfctl -srules|grep steg pass out on vxlan0 inet proto icmp from any to 192.168.179.1 scrub (steg-id) There was only 1 pinger on 192.168.179.1, I'm the only user on this box and I should have seen this in the tcpdump. Anyhow before I confuse everyone I'll just dump the code. The patches to the OS first and then the small program to ioctl the buffer into the kernel. Regards, -peter Index: sbin/pfctl/parse.y =================================================================== RCS file: /cvs/src/sbin/pfctl/parse.y,v retrieving revision 1.670 diff -u -p -u -r1.670 parse.y --- sbin/pfctl/parse.y 8 Feb 2018 09:15:46 -0000 1.670 +++ sbin/pfctl/parse.y 17 Mar 2018 19:45:13 -0000 @@ -273,6 +273,7 @@ struct filter_opts { int settos; int randomid; int max_mss; + int stegid; /* route opts */ struct { @@ -302,6 +303,7 @@ struct scrub_opts { int settos; int randomid; int reassemble_tcp; + int stegid; } scrub_opts; struct node_sc { @@ -472,7 +474,7 @@ int parseport(char *, struct range *r, i %token NOROUTE URPFFAILED FRAGMENT USER GROUP MAXMSS MAXIMUM TTL TOS DROP TABLE %token REASSEMBLE ANCHOR SYNCOOKIES %token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY RANDOMID -%token SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID +%token SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID %token ANTISPOOF FOR INCLUDE MATCHES %token BITMASK RANDOM SOURCEHASH ROUNDROBIN LEASTSTATES STATICPORT PROBABILITY %token WEIGHT BANDWIDTH FLOWS QUANTUM @@ -482,6 +484,7 @@ int parseport(char *, struct range *r, i %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW MAXPKTRATE %token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE %token DIVERTTO DIVERTREPLY DIVERTPACKET NATTO AFTO RDRTO RECEIVEDON NE LE GE +%token STEGID %token <v.string> STRING %token <v.number> NUMBER %token <v.i> PORTBINARY @@ -1085,6 +1088,13 @@ scrub_opt : NODF { } scrub_opts.randomid = 1; } + | STEGID { + if (scrub_opts.stegid) { + yyerror("steg-id cannot be respecified"); + YYERROR; + } + scrub_opts.stegid = 1; + } ; antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { @@ -1588,6 +1598,8 @@ pfrule : action dir logquick interface r.scrub_flags |= PFSTATE_NODF; if ($8.randomid) r.scrub_flags |= PFSTATE_RANDOMID; + if ($8.stegid) + r.scrub_flags |= PFSTATE_STEG; if ($8.minttl) r.min_ttl = $8.minttl; if ($8.max_mss) @@ -2147,6 +2159,7 @@ filter_opt : USER uids { filter_opts.minttl = $3.minttl; filter_opts.randomid = $3.randomid; filter_opts.max_mss = $3.maxmss; + filter_opts.stegid = $3.stegid; if ($3.reassemble_tcp) filter_opts.marker |= FOM_SCRUB_TCP; filter_opts.marker |= $3.marker; @@ -4130,7 +4143,7 @@ rule_consistent(struct pf_rule *r, int a problems++; } if (r->af == AF_INET6 && (r->scrub_flags & - (PFSTATE_NODF|PFSTATE_RANDOMID))) { + (PFSTATE_NODF|PFSTATE_RANDOMID|PFSTATE_STEG))) { yyerror("address family inet6 does not support scrub options " "no-df, random-id"); problems++; @@ -5202,6 +5215,7 @@ lookup(char *s) { "state-defaults", STATEDEFAULTS}, { "state-policy", STATEPOLICY}, { "static-port", STATICPORT}, + { "steg-id", STEGID}, { "sticky-address", STICKYADDRESS}, { "syncookies", SYNCOOKIES}, { "synproxy", SYNPROXY}, @@ -5487,6 +5501,7 @@ nodigits: if ((token = lookup(buf)) == STRING) if ((yylval.v.string = strdup(buf)) == NULL) err(1, "yylex: strdup"); + return (token); } if (c == '\n') { Index: sbin/pfctl/pfctl_parser.c =================================================================== RCS file: /cvs/src/sbin/pfctl/pfctl_parser.c,v retrieving revision 1.319 diff -u -p -u -r1.319 pfctl_parser.c --- sbin/pfctl/pfctl_parser.c 8 Feb 2018 02:26:39 -0000 1.319 +++ sbin/pfctl/pfctl_parser.c 17 Mar 2018 19:45:13 -0000 @@ -1046,6 +1046,12 @@ print_rule(struct pf_rule *r, const char printf("random-id"); ropts = 0; } + if (r->scrub_flags & PFSTATE_STEG) { + if (!ropts) + printf(" "); + printf("steg-id"); + ropts = 0; + } if (r->min_ttl) { if (!ropts) printf(" "); Index: sys/net/pf.c =================================================================== RCS file: /cvs/src/sys/net/pf.c,v retrieving revision 1.1062 diff -u -p -u -r1.1062 pf.c --- sys/net/pf.c 27 Feb 2018 09:24:56 -0000 1.1062 +++ sys/net/pf.c 17 Mar 2018 19:45:13 -0000 @@ -3483,7 +3483,8 @@ pf_rule_to_actions(struct pf_rule *r, st if (r->max_mss) a->max_mss = r->max_mss; a->flags |= (r->scrub_flags & (PFSTATE_NODF|PFSTATE_RANDOMID| - PFSTATE_SETTOS|PFSTATE_SCRUB_TCP|PFSTATE_SETPRIO)); + PFSTATE_SETTOS|PFSTATE_SCRUB_TCP|PFSTATE_SETPRIO| + PFSTATE_STEG)); if (r->scrub_flags & PFSTATE_SETPRIO) { a->set_prio[0] = r->set_prio[0]; a->set_prio[1] = r->set_prio[1]; Index: sys/net/pf_ioctl.c =================================================================== RCS file: /cvs/src/sys/net/pf_ioctl.c,v retrieving revision 1.331 diff -u -p -u -r1.331 pf_ioctl.c --- sys/net/pf_ioctl.c 8 Feb 2018 02:25:44 -0000 1.331 +++ sys/net/pf_ioctl.c 17 Mar 2018 19:45:13 -0000 @@ -1008,6 +1008,21 @@ pfioctl(dev_t dev, u_long cmd, caddr_t a NET_LOCK(); switch (cmd) { + case DIOCSETSTEG: { + int mysize; + + PF_LOCK(); + mysize = ((struct pfioc_steg *)addr)->size; + if (mysize >= sizeof(stegbuf)) { + error = EPERM; + } else { + memcpy(stegbuf,((struct pfioc_steg *)addr)->buf, mysize); + stegbufsiz = mysize; + } + PF_UNLOCK(); + break; + } + case DIOCSTART: PF_LOCK(); if (pf_status.running) Index: sys/net/pf_norm.c =================================================================== RCS file: /cvs/src/sys/net/pf_norm.c,v retrieving revision 1.209 diff -u -p -u -r1.209 pf_norm.c --- sys/net/pf_norm.c 6 Feb 2018 09:16:11 -0000 1.209 +++ sys/net/pf_norm.c 17 Mar 2018 19:45:13 -0000 @@ -1576,4 +1576,9 @@ pf_scrub(struct mbuf *m, u_int16_t flags if (flags & PFSTATE_RANDOMID && af == AF_INET && !(h->ip_off & ~htons(IP_DF))) h->ip_id = htons(ip_randomid()); + + /* steg */ + + if (flags & PFSTATE_STEG && af == AF_INET) + h->ip_id = htons(ip_stegid()); } Index: sys/net/pfvar.h =================================================================== RCS file: /cvs/src/sys/net/pfvar.h,v retrieving revision 1.476 diff -u -p -u -r1.476 pfvar.h --- sys/net/pfvar.h 9 Feb 2018 09:35:03 -0000 1.476 +++ sys/net/pfvar.h 17 Mar 2018 19:45:13 -0000 @@ -775,7 +775,8 @@ struct pf_state { #define PFSTATE_RANDOMID 0x0080 #define PFSTATE_SCRUB_TCP 0x0100 #define PFSTATE_SETPRIO 0x0200 -#define PFSTATE_SCRUBMASK (PFSTATE_NODF|PFSTATE_RANDOMID|PFSTATE_SCRUB_TCP) +#define PFSTATE_STEG 0x0400 +#define PFSTATE_SCRUBMASK (PFSTATE_NODF|PFSTATE_RANDOMID|PFSTATE_SCRUB_TCP|PFSTATE_STEG) #define PFSTATE_SETMASK (PFSTATE_SETTOS|PFSTATE_SETPRIO) u_int8_t log; u_int8_t timeout; @@ -1444,6 +1445,11 @@ struct pfioc_rule { struct pf_rule rule; }; +struct pfioc_steg { + int size; + char buf[1024]; +}; + struct pfioc_natlook { struct pf_addr saddr; struct pf_addr daddr; @@ -1593,6 +1599,7 @@ struct pfioc_synflwats { #define DIOCSTART _IO ('D', 1) #define DIOCSTOP _IO ('D', 2) +#define DIOCSETSTEG _IOWR('D', 3, struct pfioc_steg) #define DIOCADDRULE _IOWR('D', 4, struct pfioc_rule) #define DIOCGETRULES _IOWR('D', 6, struct pfioc_rule) #define DIOCGETRULE _IOWR('D', 7, struct pfioc_rule) Index: sys/netinet/ip_id.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_id.c,v retrieving revision 1.24 diff -u -p -u -r1.24 ip_id.c --- sys/netinet/ip_id.c 18 Nov 2014 02:37:31 -0000 1.24 +++ sys/netinet/ip_id.c 17 Mar 2018 19:45:13 -0000 @@ -32,6 +32,10 @@ static u_int16_t ip_shuffle[65536]; static int isindex = 0; u_int16_t ip_randomid(void); +u_int16_t ip_stegid(void); + +char stegbuf[1024]; +int stegbufsiz = 0; /* * Return a random IP id. Shuffle the new value we get into the previous half @@ -77,4 +81,20 @@ ip_randomid(void) } while (r == 0); return (r); +} + +/* ip_steg */ +u_int16_t +ip_stegid(void) +{ + static int offset; + static u_int16_t *p; + + if (offset > stegbufsiz) + offset = 0; + + p = (u_int16_t *)&stegbuf[offset]; + offset += 2; + + return (*p); } Index: sys/netinet/ip_var.h =================================================================== RCS file: /cvs/src/sys/netinet/ip_var.h,v retrieving revision 1.85 diff -u -p -u -r1.85 ip_var.h --- sys/netinet/ip_var.h 15 Nov 2017 11:48:59 -0000 1.85 +++ sys/netinet/ip_var.h 17 Mar 2018 19:45:13 -0000 @@ -214,6 +214,9 @@ extern struct pool ipqent_pool; struct route; struct inpcb; +extern int stegbufsiz; +extern char stegbuf[1024]; + int ip_ctloutput(int, struct socket *, int, int, struct mbuf *); void ip_flush(void); int ip_fragment(struct mbuf *, struct ifnet *, u_long); @@ -231,6 +234,8 @@ struct mbuf * ip_reass(struct ipqent *, struct ipq *); u_int16_t ip_randomid(void); +u_int16_t + ip_stegid(void); void ip_send(struct mbuf *); void ip_slowtimo(void); struct mbuf * -------------------------------------------------------------------------> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/fcntl.h> #include <netinet/in.h> #include <net/if.h> #include <net/pfvar.h> #include <err.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char *argv[]) { struct pfsteg { int size; char buf[1024]; } pfvar; int dev; if (argc != 2) { fprintf(stderr, "pftest 'text'\n"); exit(1); } dev = open("/dev/pf", O_RDWR); if (dev < 0) { perror("open"); exit(1); } memset(&pfvar, 0, sizeof(struct pfsteg)); pfvar.size = strlen(argv[1]); strlcpy(pfvar.buf, argv[1], sizeof(pfvar.buf)); if (ioctl(dev, DIOCSETSTEG, pfvar) < 0) { perror("ioctl"); exit(1); } close(dev); return 0; }