Packet Sniffing lab

[TOC]

Task 1.1: Sniffing Packets

#!/usr/bin/python3

from scapy.all import *

def print_pkt(pkt):
	pkt.show()

pkt = sniff(filter='icmp',prn=print_pkt)

运行截图

task1.1A

打开wireshark,进行抓包

task1.1A_1

发现确实抓到了数据包

之后我们如果不用root权限的话,直接./sniffer.py进行运行,此时

task1.1

抓取ICMP的包

#!/usr/bin/python3

from scapy.all import *

def print_pkt(pkt):
	pkt.show()

pkt = sniff(filter='icmp',prn=print_pkt)

抓取对应主机以及端口的数据包

#!/usr/bin/python3

from scapy.all import *

def print_pkt(pkt):
	pkt.show()

pkt = sniff(filter='src host 182.61.200.6 and dst port 23',prn=print_pkt)

抓取对应子网的包

#!/usr/bin/python3

from scapy.all import *

def print_pkt(pkt):
	pkt.show()

pkt = sniff(filter='host 128.230.0.0/16',prn=print_pkt)

Task 1.2: Spoofing ICMP Packets

>>> from scapy.all import *
>>> a = IP() 
>>> a.src = ’10.0.2.3>>> b = ICMP() 
>>> p = a/b 
>>> send(p)

在原来的code基础上将a.dst改为a.src就可以伪造任意分配的IP源地址,此时运行ls(a),会出现:

>>> ls(a)
version    : BitField  (4 bits)                  = 4               (4)
ihl        : BitField  (4 bits)                  = None            (None)
tos        : XByteField                          = 0               (0)
len        : ShortField                          = None            (None)
id         : ShortField                          = 1               (1)
flags      : FlagsField  (3 bits)                = <Flag 0 ()>     (<Flag 0 ()>)
frag       : BitField  (13 bits)                 = 0               (0)
ttl        : ByteField                           = 64              (64)
proto      : ByteEnumField                       = 0               (0)
chksum     : XShortField                         = None            (None)
src        : SourceIPField                       = '10.0.2.3'      (None)
dst        : DestIPField                         = '127.0.0.1'     (None)
options    : PacketListField                     = []              ([])

Task 1.3: Traceroute

提供的code

>>> a = IP()
>>> a.dst = ’1.2.3.4>>> a.ttl = 3
>>> b = ICMP()
>>> send(a/b)

在这一步中可以采用笨方法,直接是一个一个修改TTL

同时,在这一步中,由于之前对于ICMP协议的一些东西忘记,参考了一些博文,mark一下

ICMP差错报告报文

#!/usr/bin/python3

from scapy.all import *

a = IP()
a.dst = '1.2.3.4'
a.ttl = 10
b = ICMP()
while a.ttl >= 0:
	a.ttl = a.ttl - 1
	send(a/b)

之后在terminal和wireshark里面分别运行以及抓包

task1.4

task1.4.2

可以看到发送成功,并且ttl发生变化

Task 1.4: Sniffing and-then Spoofing

在这里,这个task的具体要求是:

  1. VM A要去ping一个IP为X的端系统,但是不知道X是否是alive状态,并且我们通过在VM A上的wireshark可以来截取包
  2. VM B来对于所有数据包进行嗅探,当它发现目的IP为X的数据包的时候,利用spoofing来伪造一个IP为X的response报文

在这里我选择让A去ping百度的IP地址,即X为182.61.200.6,所以在B中的sniffing&spoofing的code如下:(A是10.0.2.4;B是10.0.2.6)

#!/usr/bin/python
from scapy.all import *
def spoof_pkt(pkt):
    a = IP() 
    b = ICMP()  
    a.dst = '10.0.2.4'
    a.src = '182.61.200.6'
    send(a / b) 

pkt = sniff(filter='icmp and host 10.0.2.4', prn=spoof_pkt)

修改权限并运行py文件,在测试的时候我为了能够看出是否是B发送过来的,就没有把src的IP进行修改,即没写a.src那一行

之后在B机器上运行wireshark来获取包的内容

lab1_task1.4

可以看到在没有spoofing的数据包中,仅仅是将数据包返回,通过测试

整个运行后:

lab1_task1.4_2

完成!!!

Task Set 2: Writing Programs to Sniff and Spoof Packets

Task 2.1: Writing Packet Sniffing Program

将给出的code试运行并且进行分析后,先来看需要回答的问题

  1. Q:Please use your own words to describe the sequence of the library calls that are essential for sniffer programs. This is meant to be a summary, not detailed explanation like the one in the tutorial or book.

    A:第一步,配置机器

    在这一步中,我们首先需要的是知道我们所要sniff on的接口的名称,在sample code中,我们采用的是pacp_lookupdev函数来进行自动获取

    ​ 第二步,打开设备来进行嗅探

    在第二部的时候,我们需要创建一个嗅探会话,通过使用pcap_open_live来进行使用,其中第一个参数表示我们刚刚打开的设备的接口名称,第二个表示我们能够读取的最大字数,第三个表示模式(混杂模式置为true,非混杂置为false),to_ms表示读取超时,以毫秒为单位,最后的可作为错误信息的存储

    ​ 第三步,过滤流量

    我们并不是对于所有端口的流量都感兴趣,通常情况下,只关心特定端口的流量情况,在这一步我们分为了两个步骤,首先是对于过滤器的“compile”过程,使用的是pcap_compile函数,这个函数返回值为-1时表示false,其他都表示成功

    接下来我们要进行的时对于过滤器的应用,是pcap_setfilter函数

    ​ 第四步,实际嗅探

    在这里,有两种进行获取数据包的方式,一种是利用pcap_next()但是缺点是没有办法进行批量处理,另一种的是利用回调函数,回调函数就是每次我都会进行一个调用的函数,比如在等待用户在键盘上输入一个字符,之后我需要调用一个函数,这个函数定义了要执行的操作,之后一般我们采用pcap_loop()函数来进行抓包

    ​ 第五步,关闭会话

之后在写完并且试运行sniff的code,下面是我所获取的结果

lab1_task2.1A

其中我们可以在From和To的后面来获得对应的IP地址,同时如果不给予root权限就进行运行,会出现segmentation fault

lab1_task2.1A_2

  1. Q:Why do you need the root privilege to run a sniffer program? Where does the program fail if it is executed without the root privilege?

    A:因为我们需要root权限来打开一个sniffing会话,会卡在pcap_open_live()这里

  2. Q:Please turn on and turn off the promiscuous mode in your sniffer program. Can you demonstrate the difference when this mode is on and off? Please describe how you can demonstrate this.

    A:首先是打开(即混杂模式为true)的状态

    task2.1_3

    此时可以看到即便发出ping命令的并不是这台虚拟机,但是由于sniff的VM和发出ping的VM在一个网络中,仍然会收到数据包(),但是如果将混杂模式参数置为0,那么就只会接收到sniff的VM的收和发的数据包,而不会接收pass through的数据包

    task2.1_3_3

    task2.1_3_2

    Task 2.1B: Writing Filters

    1. 首先是ICMP的数据包,将filter_exp进行修改即可

      task2.1_4

      ​ 编译后进行运行,并且在10.0.2.6这台VM上ping 10.0.2.4

      task2.1_4_2

    2. 其次是捕获tcp包

      task2.1B

      ​ 之后编译运行,在另一台VM上运行talnet www.baidu.com命令

      task2.1_b_2

      Task 2.1C: Sniffing Passwords.

      task2.1_c

      task2.1_c_2

      task2.1_c_3

      受篇幅所限,打印出来的完整password是dees

Task 2.2: Spoofing

task2.2_2

运行截图:

task2.2_1

Task 2.2B: Spoof an ICMP Echo Request.

之后依旧采用这个方法,来对ICMP进行构造,通过第一次运行并用wireshark来进行看ICMP的checksum的值,修改后再次进行运行,可以得到echo reply

task2.2_3)task2.2-4

下面对于问题进行回答

  1. Q:Can you set the IP packet length field to an arbitrary value, regardless of how big the actual packet is?

    A:经过实验,可以发现当我们修改IP首部长度的时候,会在wireshark里面显示错误,但是如果是修改数据包长度的话,在wireshark并没有出现报错

  2. Q:Using the raw socket programming, do you have to calculate the checksum for the IP header?

    A:并不需要计算IP头部的首部校验和,因为在IP头部上TTL可能会发生改变,此时校验和也会随之发生改变

  3. Q:Why do you need the root privilege to run the programs that use raw sockets? Where does the program fail if executed without the root privilege?

Task 2.3: Sniff and then Spoof

要求:VM A要ping X,这个会产生一个ICMP的echo request报文,之后在VM B上面运行sniff_spoof程序,来检测此时的网络,当它sniff到一个echo request 报文之后,无论报文的的目的IP是什么,都立刻进行伪造一个echo reply报文,其中,echo reply报文的目的IP为A的IP,源IP为X的IP

下面是sniff_and_spoof.c的code

#include <pcap.h>
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN	6

/* Ethernet header */
struct sniff_ethernet {
        u_char  ether_dhost[ETHER_ADDR_LEN];    /* destination host address */
        u_char  ether_shost[ETHER_ADDR_LEN];    /* source host address */
        u_short ether_type;                     /* IP? ARP? RARP? etc */
};

struct sniff_ip {
        u_char  ip_vhl;                 /* version << 4 | header length >> 2 */
        u_char  ip_tos;                 /* type of service */
        u_short ip_len;                 /* total length */
        u_short ip_id;                  /* identification */
        u_short ip_off;                 /* fragment offset field */
        #define IP_RF 0x8000            /* reserved fragment flag */
        #define IP_DF 0x4000            /* dont fragment flag */
        #define IP_MF 0x2000            /* more fragments flag */
        #define IP_OFFMASK 0x1fff       /* mask for fragmenting bits */
        u_char  ip_ttl;                 /* time to live */
        u_char  ip_p;                   /* protocol */
        u_short ip_sum;                 /* checksum */
        struct  in_addr ip_src,ip_dst;  /* source and dest address */
};
#define IP_HL(ip)               (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip)                (((ip)->ip_vhl) >> 4)

/* TCP header */
typedef u_int tcp_seq;

struct sniff_tcp {
        u_short th_sport;               /* source port */
        u_short th_dport;               /* destination port */
        tcp_seq th_seq;                 /* sequence number */
        tcp_seq th_ack;                 /* acknowledgement number */
        u_char  th_offx2;               /* data offset, rsvd */
#define TH_OFF(th)      (((th)->th_offx2 & 0xf0) >> 4)
        u_char  th_flags;
        #define TH_FIN  0x01
        #define TH_SYN  0x02
        #define TH_RST  0x04
        #define TH_PUSH 0x08
        #define TH_ACK  0x10
        #define TH_URG  0x20
        #define TH_ECE  0x40
        #define TH_CWR  0x80
        #define TH_FLAGS        (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
        u_short th_win;                 /* window */
        u_short th_sum;                 /* checksum */
        u_short th_urp;                 /* urgent pointer */
};

/* ethernet headers are always exactly 14 bytes */
#define SIZE_ETHERNET 14

	const struct sniff_ethernet *ethernet; /* The ethernet header */
	const struct sniff_ip *ip; /* The IP header */
	const struct sniff_tcp *tcp; /* The TCP header */
	const char *payload; /* Packet payload */

	u_int size_ip;
	u_int size_tcp;
    u_int size_payload;
void insertip(struct in_addr addrip, char *buffer, int addr)
{
    char *ip;
    unsigned int intip;
    memcpy(&intip, &addrip, sizeof(unsigned int));
    int a = (intip >> 24) & 0xff;
    int b = (intip >> 16) & 0xff;
    int c = (intip >> 8) & 0xff;
    int d = intip & 0xff;
    char a1 = a;
    char b1 = b;
    char c1 = c;
    char d1 = d;
    strncpy(&buffer[addr], &d1, 1);
    strncpy(&buffer[addr + 1], &c1, 1);
    strncpy(&buffer[addr + 2], &b1, 1);
    strncpy(&buffer[addr + 3], &a1, 1);
}

void print_hex_ascii_line(const u_char *payload, int len, int offset)
{

	int i;
	int gap;
	const u_char *ch;

	/* offset */
	printf("%05d   ", offset);
	
	/* hex */
	ch = payload;
	for(i = 0; i < len; i++) {
		printf("%02x ", *ch);
		ch++;
		/* print extra space after 8th byte for visual aid */
		if (i == 7)
			printf(" ");
	}
	/* print space to handle line less than 8 bytes */
	if (len < 8)
		printf(" ");
	
	/* fill hex gap with spaces if not full line */
	if (len < 16) {
		gap = 16 - len;
		for (i = 0; i < gap; i++) {
			printf("   ");
		}
	}
	printf("   ");
	
	/* ascii (if printable) */
	ch = payload;
	for(i = 0; i < len; i++) {
		if (isprint(*ch))
			printf("%c", *ch);
		else
			printf(".");
		ch++;
	}

	printf("\n");

    return;
}

/*
 * print packet payload data (avoid printing binary data)
 */
void print_payload(const u_char *payload, int len)
{

	int len_rem = len;
	int line_width = 16;			/* number of bytes per line */
	int line_len;
	int offset = 0;					/* zero-based offset counter */
	const u_char *ch = payload;

	if (len <= 0)
		return;

	/* data fits on one line */
	if (len <= line_width) {
		print_hex_ascii_line(ch, len, offset);
		return;
	}

	/* data spans multiple lines */
	for ( ;; ) {
		/* compute current line length */
		line_len = line_width % len_rem;
		/* print line */
		print_hex_ascii_line(ch, line_len, offset);
		/* compute total remaining */
		len_rem = len_rem - line_len;
		/* shift pointer to remaining bytes to print */
		ch = ch + line_len;
		/* add offset */
		offset = offset + line_width;
		/* check if we have line width chars or less */
		if (len_rem <= line_width) {
			/* print last line and get out */
			print_hex_ascii_line(ch, len_rem, offset);
			break;
		}
	}

    return;
}

void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
    ethernet = (struct sniff_ethernet *)(packet);
    ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
    char* src = inet_ntoa(ip->ip_src);
    char* dst = inet_ntoa(ip->ip_dst);
    printf("got a icmp request packet src addr: %s\n", inet_ntoa(ip->ip_src)); 
    printf("dst addr :%s\nsend a reply \n", inet_ntoa(ip->ip_dst));
    int sd;
    struct sockaddr_in sin;
    sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 

    if(sd < 0) 
    {
        perror("socket() error"); 
        exit(-1);
    }
    sin.sin_family = AF_INET;

    char buffer[1024] = {
        //ip
        0x45,0x00,0x00,0x1c,
        0x00,0x00,0x00,0x00,
        0x80,0x01,0x00,0x00,

        0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,
        //icmp  
        0x00,0x00,0xff,0xff,
        0x00,0x00,0x00,0x00,
    };
    insertip(ip->ip_src,buffer,16);
    insertip(ip->ip_dst,buffer,12);
    if(sendto(sd, buffer, 20+8, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) 
	{
	    perror("sendto() error"); 
	    exit(-1);
	}
}

int main()
{

	char *dev;			/* capture device name */
	char errbuf[PCAP_ERRBUF_SIZE];		/* error buffer */
	pcap_t *handle;				/* packet capture handle */

	char filter_exp[] = "icmp and icmp[0] = 8";		/* filter expression [3] */
	struct bpf_program fp;			/* compiled filter program (expression) */
	bpf_u_int32 mask;			/* subnet mask */
	bpf_u_int32 net;			/* ip */
	struct pcap_pkthdr header;
    const u_char *packet;
    /* find a capture device if not specified on command-line */
	dev = pcap_lookupdev(errbuf);
	if (dev == NULL) {
		fprintf(stderr, "Couldn't find default device: %s\n",errbuf);
		return (2);
	}
	
	/* get network number and mask associated with capture device */
	if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
		fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
		net = 0;
		mask = 0;
	}

	/* print capture info */
	printf("Device: %s\n", dev);
	printf("Filter expression: %s\n", filter_exp);

	/* open capture device */
	handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
	if (handle == NULL) {
		fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
		return(2);
	}

	/* compile the filter expression */
	if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
		fprintf(stderr, "Couldn't parse filter %s: %s\n",
		    filter_exp, pcap_geterr(handle));
		return(2);
	}

	/* apply the compiled filter */
	if (pcap_setfilter(handle, &fp) == -1) {
		fprintf(stderr, "Couldn't install filter %s: %s\n",
		    filter_exp, pcap_geterr(handle));
		return(2);
	}

	/* now we can set our callback function */
	pcap_loop(handle, -1, got_packet, NULL);
	pcap_close(handle);

	printf("\nCapture complete.\n");

return 0;
}

其中,一些数据结构来自于网站提供的sniffex.c,insertip()函数的作用是将VM A发送的IP的源地址与目的地址分别作为VM B构造的ICMP报文的目的地址与源地址,之后,当执行回调函数got_packet的时候,需要在回调函数got_packet里面调用insert()函数,下面是运行结果。

task2.3_3

现在VM A上面ping一个地址

task2.3_1

在VM B上编译运行sniff_and_spoof.c

task2.3_2

之后在VM B上面运行wireshark抓包,可以看到有echo reply


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

Bufferr_Overflow lab 上一篇
8/9CTF一周学习总结 下一篇