Skip to content

Instantly share code, notes, and snippets.

@huzhifeng
Last active August 29, 2015 14:07
Show Gist options
  • Save huzhifeng/93d2b12eb14b88ad65c8 to your computer and use it in GitHub Desktop.
Save huzhifeng/93d2b12eb14b88ad65c8 to your computer and use it in GitHub Desktop.
A linux kernel module to snoop HTTP based on Netfilter
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/netfilter_ipv4.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("[email protected]>");
MODULE_DESCRIPTION("Netfilter HTTP sniffer module");
#define MAX_HTTP_HEADER_LEN 2048
#define MAX_HTTP_DATA_LEN 1024
#if 1
#define LKP(fmt, args...) printk(KERN_EMERG "[LKP %s(), line %d, " fmt "]\n", __FUNCTION__, __LINE__, ##args)
#else
#define LKP(fmt, args...)
#endif
typedef enum {
HTTP_GET = 0,
HTTP_POST,
HTTP_HEAD,
HTTP_DELETE,
HTTP_INVALID
}HTTP_TYPE;
typedef struct _http_request {
HTTP_TYPE type;
char *url;
char *host;
char *ua;
char *cookie;
char *data;
}http_request;
static int victim_ip = 0x0; // Only match the last byte
static int debug = 0;
module_param(victim_ip, int, 0444);
module_param(debug, bool, 0444);
static unsigned int http_sniffer_fn(unsigned int hooknum,
struct sk_buff *pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct ethhdr *ethh = NULL;
struct iphdr *iph = NULL;
struct tcphdr *tcph = NULL;
u32 *httph = NULL;
http_request *req = NULL;
int tcp_dst_port;
int tcp_data_len=0;
unsigned char *tcp_data = NULL;
unsigned char *http_data = NULL;
unsigned char c = 0;
unsigned char header_buf[MAX_HTTP_HEADER_LEN] = {0};
unsigned int url_len = 0;
unsigned int host_len = 0;
unsigned int ua_len = 0;
unsigned int cookie_len = 0;
unsigned int content_length = 0;
int i = 0, j = 0;
if (NULL == pskb) {
return NF_ACCEPT;
}
ethh = eth_hdr(pskb);
if (NULL == ethh) {
return NF_ACCEPT;
}
iph = ip_hdr(pskb);
if (NULL == iph) {
return NF_ACCEPT;
}
if (0x6 != iph->protocol) { // Not TCP
return NF_ACCEPT;
}
if (victim_ip && ((iph->saddr & 0xFF) != (victim_ip & 0xFF))) {
return NF_ACCEPT;
}
tcph = (struct tcphdr *)((char *)iph + (iph->ihl << 2));
if (NULL == tcph) {
return NF_ACCEPT;
}
tcp_dst_port = ntohs(tcph->dest);
if (80 != tcp_dst_port) {
return NF_ACCEPT;
}
if (!tcph->psh) { // TCP three-way handshake
return NF_ACCEPT;
}
tcp_data_len = iph->tot_len - (iph->ihl << 2) - (tcph->doff << 2);
tcp_data = (unsigned char *)(pskb->data + (iph->ihl << 2) + (tcph->doff << 2));
httph = (u32 *)tcp_data;
if (*httph != 0x504f5354) { // Not POST
return NF_ACCEPT;
}
req = kmalloc(sizeof(http_request), GFP_KERNEL);
if (!req) {
return NF_ACCEPT;
}
memset(req, 0x0, sizeof(http_request));
while ((i < tcp_data_len) && tcp_data[i]) {
c = tcp_data[i];
if ((c == 0xd) && (i + 1 < tcp_data_len) && (tcp_data[i + 1] == 0xa)) {
if (j == 0) {
break;
}
header_buf[j] = 0;
if (strstr(header_buf, "POST ") != NULL) {
req->type = HTTP_POST;
url_len = j - 14;
req->url = kmalloc(url_len + 1, GFP_KERNEL);
if (!req->url) {
goto req_free;
}
memset(req->url, 0x0, url_len + 1);
memcpy(req->url, header_buf + 5, url_len);
if (debug) LKP("Post: %s", req->url);
} else if (strstr(header_buf, "Host: ") != NULL) {
host_len = j - 6;
req->host = kmalloc(host_len + 1, GFP_KERNEL);
if (!req->host) {
goto req_free;
}
memset(req->host, 0x0, host_len + 1);
memcpy(req->host, header_buf + 6, host_len);
if (debug) LKP("Host: %s", req->host);
} else if (strstr(header_buf, "User-Agent: ") != NULL) {
ua_len = j - 12;
req->ua = kmalloc(ua_len + 1, GFP_KERNEL);
if (!req->ua) {
goto req_free;
}
memset(req->ua, 0x0, ua_len + 1);
memcpy(req->ua, header_buf + 12, ua_len);
if (debug) LKP("User-Agent: %s", req->ua);
} else if (strstr(header_buf, "Cookie: ") != NULL) {
cookie_len = j - 8;
req->cookie = kmalloc(cookie_len + 1, GFP_KERNEL);
if (!req->cookie) {
goto req_free;
}
memset(req->cookie, 0x0, cookie_len + 1);
memcpy(req->cookie, header_buf + 8, cookie_len);
if (debug) LKP("Cookie: %s", req->cookie);
} else if (strstr(header_buf, "Content-Length: ") != NULL) {
content_length = (unsigned int)simple_strtoul(header_buf + 16, NULL, 10);
if (debug) LKP("Content-Length: %d", content_length);
} else if (strstr(header_buf, "Content-Type: application/x-www-form-urlencoded") != NULL) {
req->type = HTTP_POST;
if (debug) LKP("%s", header_buf);
} else {
//if (debug) LKP("Header: %s", header_buf);
}
j = 0;
i++;
}
else {
header_buf[j++]=c;
}
i++;
}
if (HTTP_POST == req->type) {
if (debug) {
#define D(i) (ethh->h_dest[i])
#define S(i) (ethh->h_source[i])
LKP("ETH dump: dst_mac=%02x:%02x:%02x:%02x:%02x:%02x,src_mac=%02x:%02x:%02x:%02x:%02x:%02x,type=0x%x",\
D(0),D(1),D(2),D(3),D(4),D(5),S(0),S(1),S(2),S(3),S(4),S(5),ethh->h_proto);
#undef D
#undef S
LKP("IP dump:version=%d,ihl=%d,tos=%d,tot_len=%d,id=0x%x,frag_off=0x%x,ttl=%d,protocol=%d,check=0x%x,saddr=0x%x,daddr=0x%x",\
iph->version,iph->ihl,iph->tos,iph->tot_len,iph->id,iph->frag_off,iph->ttl,iph->protocol,iph->check,iph->saddr,iph->daddr);
LKP("TCP dump:source=%d,dest=%d,seq=%d,ack_seq=%d,doff=%d,psh=%d,window=%d,check=0x%x,urg_ptr=0x%x",\
tcph->source,tcph->dest,tcph->seq,tcph->ack_seq,tcph->doff,tcph->psh,tcph->window,tcph->check,tcph->urg_ptr);
LKP("HTTP dump:content_length=%d,tcp_data_len=%d", content_length, tcp_data_len);
}
if (content_length > MAX_HTTP_DATA_LEN) {
LKP("Too big data, content_length = %d", content_length);
goto req_free;
}
http_data = (unsigned char *)strstr(tcp_data, "\r\n\r\n");
if ((content_length > 0) && (http_data + 4 - tcp_data < tcp_data_len)) {
req->data = kmalloc(content_length + 1, GFP_KERNEL);
if (!req->data) {
goto req_free;
}
http_data += 4;
memset(req->data, 0x0, content_length + 1);
memcpy(req->data, http_data, content_length);
LKP("0x%08x POST %s at %s: %s", iph->saddr, req->url, req->host, req->data);
} else {
LKP("No HTTP Post data");
}
}
req_free:
if (req) {
if (req->url) {
kfree(req->url);
}
if (req->host) {
kfree(req->host);
}
if (req->ua) {
kfree(req->ua);
}
if (req->cookie) {
kfree(req->cookie);
}
if (req->data) {
kfree(req->data);
}
kfree(req);
}
return NF_ACCEPT;
}
static struct nf_hook_ops http_sniffer_ops __read_mostly = {
.hook = http_sniffer_fn,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_CONNTRACK-1,
};
static int __init http_sniffer_init(void)
{
LKP("enter");
return nf_register_hook(&http_sniffer_ops);
}
static void __exit http_sniffer_exit(void)
{
LKP("enter");
nf_unregister_hook(&http_sniffer_ops);
}
module_init(http_sniffer_init);
module_exit(http_sniffer_exit);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment