1. Task Set 1
-
这部分主要是利用工具来嗅探数据包,这里用的是 scapy。
- Scapy是功能强大的交互式数据包处理程序。它能够伪造或解码各种协议的数据包,在线发送,捕获,匹配请求和响应等。它可以轻松处理大多数经典任务,例如扫描,跟踪路由,探测,单元测试,攻击或网络发现,它可以代替hping,arpspoof,arp-sk,arping,p0f甚至Nmap,tcpdump和tshark的某些部分。它在其他工具无法处理的许多其他特定任务上也表现出色,例如发送无效帧,组合技术(VLAN跳变+ ARP缓存中毒,WEP加密通道上的VOIP解码等等)
-
可以用下面的命令进行安装。
sudo pip3 install scapy
-
当我们使用pip安装python库的时候,经常会碰到这种情况,提示以“root”用户身份运行 pip 可能会导致权限损坏和冲突,往往因为这个问题,导致我们无法更新pip或下载库失败
-
这个问题的解决方案是建立一个 虚拟环境中,自所载目录树,其中包含一个Python安装一个 特别版本的蟒蛇,再加上一些额外的软件包。
-
不同的应用程序,然后可以使用不同的虚拟环境。 要解决的前例的相互冲突的要求, 应用程序可以有其自己的虚拟环境中的版本1.0 安装应用程序,同时B还有另一个虚拟环境与2.0版本。 如果应用程序B需要一个图书馆升级为3.0版,这将 在不影响申请的环境。
-
我们只需要简单的两条命令就可以解决这个问题
1.寻找路径
首先我们需要找到我们的pip安装位置,然后cd过去
find / -name pip-*
2.创建虚拟环境
python3 -m venv tutorial-env
- 可能会出现以下情况
- 出错原因:无法创建虚拟环境,因为ensurpip不可用,需要安装python3-venv包
- 解决办法:
apt install python3.11-venv
- 解决办法:
- 再次创建即可。
3.激活
source tutorial-env/bin/activate
4.再次更新
python -m pip install novas
pip install --upgrade pip
-
检测一下:
-
再次运行指令下载scapy
-
简单测试一下,构造了一个IP数据包并打印其的一些信息
-
我们熟悉一下他的用法:
- 构造IP包,发送给了我的虚拟机192.168.163.128:
- 可以看到我们的虚拟机的确收到了ttl为64的数据包:
- 构造IP包,发送给了我的虚拟机192.168.163.128:
-
此处IP创建了一个默认的数据包,使用ls(IP())可以查看IP数据包的参数
-
面向192.168.163.128的440到443端口发送单个SYN包,在接受单个响应时退出。目标主机返回一个“SA”或者SYN-ACK标志标明端口是开放的。
-
具体详细的使用我参考了文档https://blog.sina.com.cn/s/blog_5c92dd1f0102w2vf.html
Task 1.1 嗅探数据包
- 下面是使用 scapy来嗅探数据包的一个例子
#!/usr/bin/python3
from scapy.all import *
def print_pkt(pkt):
pkt.show()
pkt = sniff(filter="icmp",prn=print_pkt)
Task 1.1A
- 先使用
chmod a+x sniffer.py
添加执行权限。先使用root权限运行上面的程序 sudo ./sniffer.py, 再在另一个终端ping一个网站,或者随便打开一个网页。 - 结果如下,可以看到其成功嗅探到了不同协议的数据包,图中只包括ICMP包。
Task 1.1B
- 一般在嗅探包时我们只对特定类型的数据包感兴趣,所以我们需要对数据包进行一些过滤。scapy的过滤机制使用BPF的语法,这部分我们需要实现几个过滤的方法。
- 上面的代码就是只捕捉icmp的数据包,略修改如下:
#!/usr/bin/python3
from scapy.all import *
def print_pkt(pkt):
return pkt.summary()
pkt = sniff(filter="icmp",prn=print_pkt)
print(pkt)
-
运行结果如下,可以看到捕获到了ICMP数据包
-
2.只捕捉来自特定IP,且目标端口号为23的TCP数据包
查看自己的IP地址如下:
-
这里随意拿个IP来测试, 我们使用 192.168.163.7, 嗅探代码如下:
#!/usr/bin/python3
from scapy.all import *
def print_pkt(pkt):
return pkt.summary()
pkt = sniff(filter="tcp and src host 192.168.163.7 and dst port 23",prn=print_pkt)
print(pkt)
- 发送数据包的代码如下:
#!/usr/bin/python3
from scapy.all import *
ip = IP()
ip.src = "192.168.163.7"
ip.dst = "192.168.163.1"
tcp = TCP()
tcp.dport = 23
send(ip/tcp)
- 运行嗅探的程序,再运行发送数据包的程序(可以多试几次), 结果如下:
-
- 捕捉来自或发送到特定子网的数据包
- 这里我们使用的子网为128.230.0.0/16.嗅探代码如下:
#!/usr/bin/python3
from scapy.all import *
def print_pkt(pkt):
return pkt.summary()
pkt = sniff(filter="net 128.230.0.0/16",prn=print_pkt)
print(pkt)
- 发送包的代码如下:
#!/usr/bin/python3
from scapy.all import *
ip = IP()
ip.src = "192.168.1.7"
ip.dst = "128.230.0.1"
tcp = TCP()
tcp.dport = 23
send(ip/tcp)
ip.src = "128.230.0.1"
ip.dst = "192.168.1.7"
send(ip/tcp)
- 嗅探结果如下,可以看到嗅探到了发送给子网128.230.0.0/16 和 该子网发送过来的数据包。
Task 1.2
-
这部分主要是伪造任意的IP地址发IP包,这里我们用的是ICMP协议,使用的IP地址为192.168.163.189, 注意 在task 1.1B中,我们查看了自己的IP地址为192.168.163.190,也就是这里我们伪造成ip地址为 192.168.163.189进行发包。
-
先启动wireshark,选择网卡eth0,再运行发包的程序
-
发送的代码如下:
#!/usr/bin/python3
from scapy.all import *
ip = IP()
ip.src = "192.168.163.159"
ip.dst = "128.230.0.1"
icmp = ICMP()
send(ip/icmp)
- 在wireshark中可看到记录:
Task 1.3
-
这部分主要是实现traceroute来估计本机到指定IP之间的路由器的个数。
-
此任务的目的是使用Scapy来估计虚拟机和选定目的地之间的路由器数量方面的距离。这基本上是由跟踪工具实现的。在这个任务中,我们将编写我们自己的工具。这个想法非常简单:只需发送一个数据包(任何类型)到目的地,首先将其实时时间(TTL)字段设置为1。这个数据包将被第一个路由器丢弃,该路由器将向我们发送一个ICMP错误消息,告诉我们它的运行时间已经超过。这就是我们如何得到第一个路由器的IP地址。然后,我们将TTL字段增加到2,发送另一个数据包,并获得第二个路由器的IP地址。我们将重复此过程,直到我们的数据包最终到达目的地。需要注意的是,这个实验只得到一个估计结果,因为理论上,不是所有这些包采取相同的路径(但在实际中,它们可能在短时间内)。下面的代码显示了这个过程中的一轮。
#!/usr/bin/python3
from scapy.all import *
import sys
def traceroute(target, minttl=1, maxttl=30, dport=80):
print("target: %s(port=%s)" % (target, dport))
ans, unans = sr(IP(dst=target, ttl=(minttl,maxttl),id=RandShort())/TCP(flags=0x2, dport=dport), timeout=10)
for snd,rcv in ans:
print(snd.ttl, rcv.src)
if __name__ == '__main__':
if len(sys.argv) <= 1:
traceroute("baidu.com")
else:
traceroute(sys.argv[1])
//模拟一个traceroute,循环每次TTL+1,中间节点都发回ICMP TTL字段过期的错误信息,目的节点发回ICMP reply就结束:
- 运行结果如下图所示, 可以看到打印不同TTL对应的IP:
Task 1.4: Sniffing and-then Spoofing
-
需要在同一局域网上的两台机器:虚拟机和用户容器。从用户容器中,您可以生成一个IPX。这将生成一个ICMP回波请求包。如果X被激活,ping程序将收到一个回波响应,并打印出响应。您的嗅探和欺骗程序运行在VM上,它通过数据包嗅探来监视局域网。每当它看到ICMP回波请求时,无论目标IP地址是什么,您的程序都应该立即使用数据包欺骗技术发送回波回复。因此,无论机器X是否活动,ping程序将始终收到回复,表明X激活。您需要使用Scapy来完成此任务。在您的报告中,您需要提供证据来证明您的技术是有效的。在您的实验中,您应该从用户容器中获取以下三个IP地址。报告你的观察结果,并解释这些结果。
-
简单来说,这部分主要是准备两个在同一个局域网的虚拟机,一个机器ping任意IP x,另一个机器伪造ICMP回复请求,使得其有回复,而IP x所对应的机器可能根本不存在。
-
我们准备的A机器 IP 地址为192.168.163.4,B机器IP地址为192.168.163.5,我们用A机器去发送请求,B机器伪造响应。
-
代码如下:
#!/usr/bin/python3
from scapy.all import *
def print_pkt(pkt):
send(IP(src=pkt[IP].dst, dst=pkt[IP].src)/ICMP(type="echo-reply", code= 0, id=pkt[ICMP].id, seq=pkt[ICMP].seq))
pkt = sniff(filter="icmp[icmptype]==icmp-echo",prn=print_pkt)
- 我们再去ping一个ip 1.1.1.2,结果还有回应。我们把那个程序关掉,出现了正常的真回应,可以看出还是有区别的。
- 在断网情况下,仍然可以ping通其他网段的IP
Task 2.1
Task 2.1A: Understanding How a Sniffer Works
- 可以使用pcap库很容易地编写嗅探器程序。有了pcap,嗅探器的任务就变成了调用pcap库中的一个简单的过程序列。在序列的最后,一旦数据包被捕获,就将被放入缓冲区中进行进一步处理。数据包捕获的所有细节都由pcap库处理。SEED的书提供了一个示例代码,展示了如何使用pcap编写一个简单的嗅探器程序。我们在下面包括了示例代码(详细说明请参阅本书)。
- 在这个任务中,学生需要编写一个嗅探器程序来打印出每个捕获的数据包的源和目标IP地址。学生可以输入上面的代码或从SEED书的网站(https://www.handsonsecurity。net/figurecode.html)。学生应该提供屏幕截图作为证据,以表明他们的嗅探程序可以成功运行,并产生预期的结果。
- 代码如下:
#include <pcap.h>
#include <stdio.h>
#include <arpa/inet.h>
/* Ethernet header */
struct ethheader {
u_char ether_dhost[6]; /* destination host address */
u_char ether_shost[6]; /* source host address */
u_short ether_type; /* protocol type (IP, ARP, RARP, etc) */
};
/* IP Header */
struct ipheader {
unsigned char iph_ihl:4, //IP header length
iph_ver:4; //IP version
unsigned char iph_tos; //Type of service
unsigned short int iph_len; //IP Packet length (data + header)
unsigned short int iph_ident; //Identification
unsigned short int iph_flag:3, //Fragmentation flags
iph_offset:13; //Flags offset
unsigned char iph_ttl; //Time to Live
unsigned char iph_protocol; //Protocol type
unsigned short int iph_chksum; //IP datagram checksum
struct in_addr iph_sourceip; //Source IP address
struct in_addr iph_destip; //Destination IP address
};
void got_packet(u_char *args, const struct pcap_pkthdr *header,
const u_char *packet)
{
struct ethheader *eth = (struct ethheader *)packet;
if (ntohs(eth->ether_type) == 0x0800) { // 0x0800 is IP type
struct ipheader * ip = (struct ipheader *)
(packet + sizeof(struct ethheader));
printf(" From: %s\n", inet_ntoa(ip->iph_sourceip));
printf(" To: %s\n", inet_ntoa(ip->iph_destip));
/* determine protocol */
switch(ip->iph_protocol) {
case IPPROTO_TCP:
printf(" Protocol: TCP\n\n");
return;
case IPPROTO_UDP:
printf(" Protocol: UDP\n\n");
return;
case IPPROTO_ICMP:
printf(" Protocol: ICMP\n\n");
return;
default:
printf(" Protocol: others\n\n");
return;
}
}
}
int main()
{
pcap_t *handle;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
char filter_exp[] = "icmp and src host 110.242.68.4 and dst host 192.168.163.190";
bpf_u_int32 net;
// Step 1: Open live pcap session on NIC with name enp0s3
handle = pcap_open_live("enp0s3", BUFSIZ, 1, 1000, errbuf);
printf("listening on network card, ret: %p...\n", handle);
// Step 2: Compile filter_exp into BPF psuedo-code
printf("try to compile filter...\n");
pcap_compile(handle, &fp, filter_exp, 0, net);
printf("try to set filter...\n");
pcap_setfilter(handle, &fp);
// Step 3: Capture packets
printf("start to sniff...\n");
pcap_loop(handle, -1, got_packet, NULL);
pcap_close(handle); //Close the handle
return 0;
}
- 使用如下命令编译:
gcc -o sniff sniff.c -lpcap
- 发现没有pcap.h这个包,上网查找解决办法:
sudo apt-get install libpcap0.8-dev
- 最好安装完,重新加载一下
sudo ldconfig
- 运行,并尝试ping baidu.com,可以看到发送的包出现在结果中:
Task 2.1B: Writing Filters
-
这部分主要是写一些过滤器。这部分还是复用 task 2.1A的代码,只是修改其中的过滤器而已。Pcap过滤器的例子:
-
1.只捕捉两个特定主机之间的ICMP包,使用的过滤器为 icmp and src host 192.168.163.190 and dst host 192.168.163.128只捕捉从发送到192.168.163.128的ICMP包。
-
结果如下,可以看到全是从192.168.163.190发送到192.168.163.128的ICMP包,没有其他类型的包。
-
2.捕捉目的端口在10到100之间的TCP包
-
使用的过滤器为
tcp and dst portrange 10-100
, 只捕捉从发送到的ICMP包。结果如下,可以看到全是从发送到的ICMP包,没有其他类型的包。
-
结果如下,访问qq网站的包出现在了下面的报告中
-
而访问192.168.163.128:8080的包却没有出现
Task2.1C: Sniffing Passwords
- 当普通用户发送数据包时,操作系统通常不允许用户设置协议头中的所有字段(如TCP、UDP和IP头)。操作系统将设置大部分字段,而只允许用户设置少数字段,如目标IP地址、目标端口号等。但是,如果用户具有根权限,他们可以在数据包头中设置任何任意字段。这被称为数据包欺骗,它可以通过原始套接字来完成。原始套接字为程序员提供了对数据包构造的绝对控制,允许程序员构造任何任意的数据包,包括设置头字段和有效负载。使用原始套接字非常简单;它涉及四个步骤:
(1)创建一个原始套接字
(2)集套接字选项
(3)构建数据包
(4)通过原始套接字发送数据包。
-
有许多在线教程可以教您如何在C编程中使用原始套接字。我们已经将一些教程链接到该实验室的网页。请阅读它们,并学习如何编写一个数据包欺骗程序。我们展示了这样一个程序的一个简单的骨架。
-
嗅探密码。请说明当有人在您监控的网络上使用telnet时,您如何使用嗅探器程序来捕获密码。您可能需要修改您的嗅探器代码来打印出捕获的TCP数据包的数据部分(telnet使用TCP)。如果您打印出整个数据部分,然后手动标记密码(或部分密码)的位置,这是可以接受的。
-
这部分是用嗅探去捕捉telent协议中的密码。我们使用scapy会更方便一些。
-
代码如下:
#!/usr/bin/python3
from scapy.all import *
def print_pkt(pkt):
pkt.show()
print(sniff(filter="tcp port 23", prn=print_pkt))
-
然后 使用telnet 192.168.163.7 。嗅探到的密码如下,其分成了几个包发送,如下:
-
这边可以看到运行代码之后对tcp包的截获情况
Task 2.2
- 这部分主要是伪造包。用到的一些代码文件如下:
myheader.h
内容如下:
/* Ethernet header */
struct ethheader {
u_char ether_dhost[6]; /* destination host address */
u_char ether_shost[6]; /* source host address */
u_short ether_type; /* IP? ARP? RARP? etc */
};
/* IP Header */
struct ipheader {
unsigned char iph_ihl:4, //IP header length
iph_ver:4; //IP version
unsigned char iph_tos; //Type of service
unsigned short int iph_len; //IP Packet length (data + header)
unsigned short int iph_ident; //Identification
unsigned short int iph_flag:3, //Fragmentation flags
iph_offset:13; //Flags offset
unsigned char iph_ttl; //Time to Live
unsigned char iph_protocol; //Protocol type
unsigned short int iph_chksum; //IP datagram checksum
struct in_addr iph_sourceip; //Source IP address
struct in_addr iph_destip; //Destination IP address
};
/* ICMP Header */
struct icmpheader {
unsigned char icmp_type; // ICMP message type
unsigned char icmp_code; // Error code
unsigned short int icmp_chksum; //Checksum for ICMP Header and data
unsigned short int icmp_id; //Used for identifying request
unsigned short int icmp_seq; //Sequence number
};
/* UDP Header */
struct udpheader
{
u_int16_t udp_sport; /* source port */
u_int16_t udp_dport; /* destination port */
u_int16_t udp_ulen; /* udp length */
u_int16_t udp_sum; /* udp checksum */
};
/* TCP Header */
struct tcpheader {
u_short tcp_sport; /* source port */
u_short tcp_dport; /* destination port */
u_int tcp_seq; /* sequence number */
u_int tcp_ack; /* acknowledgement number */
u_char tcp_offx2; /* data offset, rsvd */
#define TH_OFF(th) (((th)->tcp_offx2 & 0xf0) >> 4)
u_char tcp_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 tcp_win; /* window */
u_short tcp_sum; /* checksum */
u_short tcp_urp; /* urgent pointer */
};
/* Psuedo TCP header */
struct pseudo_tcp
{
unsigned saddr, daddr;
unsigned char mbz;
unsigned char ptcl;
unsigned short tcpl;
struct tcpheader tcp;
char payload[1500];
};
checksum.c
内容如下:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include "myheader.h"
unsigned short in_cksum (unsigned short *buf, int length)
{
unsigned short *w = buf;
int nleft = length;
int sum = 0;
unsigned short temp=0;
/*
* The algorithm uses a 32 bit accumulator (sum), adds
* sequential 16 bit words to it, and at the end, folds back all
* the carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* treat the odd byte at the end, if any */
if (nleft == 1) {
*(u_char *)(&temp) = *(u_char *)w ;
sum += temp;
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); // add hi 16 to low 16
sum += (sum >> 16); // add carry
return (unsigned short)(~sum);
}
/****************************************************************
TCP checksum is calculated on the pseudo header, which includes
the TCP header and data, plus some part of the IP header.
Therefore, we need to construct the pseudo header first.
*****************************************************************/
unsigned short calculate_tcp_checksum(struct ipheader *ip)
{
struct tcpheader *tcp = (struct tcpheader *)((u_char *)ip +
sizeof(struct ipheader));
int tcp_len = ntohs(ip->iph_len) - sizeof(struct ipheader);
/* pseudo tcp header for the checksum computation */
struct pseudo_tcp p_tcp;
memset(&p_tcp, 0x0, sizeof(struct pseudo_tcp));
p_tcp.saddr = ip->iph_sourceip.s_addr;
p_tcp.daddr = ip->iph_destip.s_addr;
p_tcp.mbz = 0;
p_tcp.ptcl = IPPROTO_TCP;
p_tcp.tcpl = htons(tcp_len);
memcpy(&p_tcp.tcp, tcp, tcp_len);
return (unsigned short) in_cksum((unsigned short *)&p_tcp,
tcp_len + 12);
}
spoof.c
内容如下:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include "myheader.h"
/*************************************************************
Given an IP packet, send it out using a raw socket.
**************************************************************/
void send_raw_ip_packet(struct ipheader* ip)
{
struct sockaddr_in dest_info;
int enable = 1;
// Step 1: Create a raw network socket.
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
printf("sock: %d\n", sock);
// Step 2: Set socket option.
setsockopt(sock, IPPROTO_IP, IP_HDRINCL,
&enable, sizeof(enable));
// Step 3: Provide needed information about destination.
dest_info.sin_family = AF_INET;
dest_info.sin_addr = ip->iph_destip;
// Step 4: Send the packet out.
sendto(sock, ip, ntohs(ip->iph_len), 0,
(struct sockaddr *)&dest_info, sizeof(dest_info));
close(sock);
}
Task 2.2A
- 这部分主要是伪造IP包。这里伪造是UDP包, 代码如下:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include "myheader.h"
void send_raw_ip_packet (struct ipheader* ip);
/******************************************************************
Spoof a UDP packet using an arbitrary source IP Address and port
*******************************************************************/
int main() {
char buffer[1500];
memset(buffer, 0, 1500);
struct ipheader *ip = (struct ipheader *) buffer;
struct udpheader *udp = (struct udpheader *) (buffer +
sizeof(struct ipheader));
/*********************************************************
Step 1: Fill in the UDP data field.
********************************************************/
char *data = buffer + sizeof(struct ipheader) +
sizeof(struct udpheader);
const char *msg = "Hello Server!\n";
int data_len = strlen(msg);
strncpy (data, msg, data_len);
/*********************************************************
Step 2: Fill in the UDP header.
********************************************************/
udp->udp_sport = htons(12345);
udp->udp_dport = htons(9090);
udp->udp_ulen = htons(sizeof(struct udpheader) + data_len);
udp->udp_sum = 0; /* Many OSes ignore this field, so we do not
calculate it. */
/*********************************************************
Step 3: Fill in the IP header.
********************************************************/
ip->iph_ver = 4;
ip->iph_ihl = 5;
ip->iph_ttl = 20;
ip->iph_sourceip.s_addr = inet_addr("1.1.1.1");
ip->iph_destip.s_addr = inet_addr("8.8.8.8");
ip->iph_protocol = IPPROTO_UDP; // The value is 17.
ip->iph_len = htons(sizeof(struct ipheader) +
sizeof(struct udpheader) + data_len);
/*********************************************************
Step 4: Finally, send the spoofed packet
********************************************************/
send_raw_ip_packet (ip);
return 0;
}
- 使用
gcc -o task22A task22A.c spoof.c -lpcap
编译,./task22A运行,查看后台wireshark,可以看到我们伪造的UDP包
Task 2.2B
- 这部分是伪造ICMP Echo请求。伪造的代码如下, 其中源IP 192.168.是局域网内另一个虚拟机的IP,这里我使用了我的ubuntu的ip地址:192.168.163.128
- 代码如下:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include "myheader.h"
unsigned short in_cksum (unsigned short *buf, int length);
void send_raw_ip_packet(struct ipheader* ip);
/******************************************************************
Spoof an ICMP echo request using an arbitrary source IP Address
*******************************************************************/
int main() {
char buffer[1500];
memset(buffer, 0, 1500);
/*********************************************************
Step 1: Fill in the ICMP header.
********************************************************/
struct icmpheader *icmp = (struct icmpheader *)
(buffer + sizeof(struct ipheader));
icmp->icmp_type = 8; //ICMP Type: 8 is request, 0 is reply.
// Calculate the checksum for integrity
icmp->icmp_chksum = 0;
icmp->icmp_chksum = in_cksum((unsigned short *)icmp,
sizeof(struct icmpheader));
/*********************************************************
Step 2: Fill in the IP header.
********************************************************/
struct ipheader *ip = (struct ipheader *) buffer;
ip->iph_ver = 4;
ip->iph_ihl = 5;
ip->iph_ttl = 20;
ip->iph_sourceip.s_addr = inet_addr("192.168.163.128");
ip->iph_destip.s_addr = inet_addr("8.8.8.8");
ip->iph_protocol = IPPROTO_ICMP;
ip->iph_len = htons(sizeof(struct ipheader) +
sizeof(struct icmpheader));
/*********************************************************
Step 3: Finally, send the spoofed packet
********************************************************/
send_raw_ip_packet (ip);
return 0;
}
- 可以看到wireshark中记录到了伪造的虚拟机发送的request消息。
Q4: 能把IP包的长度设置为任意数值,而不管实际的包的大小吗?
A4:将代码中设置长度该成下面的代码,运行可知其为28B,修改长度为10B,wireshark没有捕捉到包,说明没有发出去。
ip->iph_len = htons(10);
printf("iph_len: %d\n", sizeof(struct ipheader) +
sizeof(struct icmpheader));
- 可见wireshark没有捕捉到,说明没有发出去。
- 修改成1000B,结果如下,可以看到正常发送出去。说明可以调大length,不能调小length。
Q5: 使用 raw socket 编程, 我们要计算IP头部的checksum吗?
A5: 不用计算IP头部的checksum,但是需要计算ICMP头部的checksum。
Q6: 为什么使用raw socket 编程需要root权限?没有root权限执行时程序会在哪里报错?
A6:因为能任意读取发送包意味着很大的安全风险,所以需要root权限。
Task 2.3
-
准备两个在同一个局域网的虚拟机,这部分主要是同时嗅探和伪造包,实现一个机器ping任意IP x,另一个机器伪造ICMP回复请求,使得其有回复,而IP x所对应的机器可能根本不存在。
-
代码如下:
#include <pcap.h>
#include <stdio.h>
#include <arpa/inet.h>
#include "myheader.h"
void got_packet(u_char *args, const struct pcap_pkthdr *header,
const u_char *packet)
{
struct ethheader *eth = (struct ethheader *)packet;
if (ntohs(eth->ether_type) == 0x0800) { // 0x0800 is IP type
struct ipheader * ip = (struct ipheader *)
(packet + sizeof(struct ethheader));
printf("From: %s ", inet_ntoa(ip->iph_sourceip));
printf("To: %s ", inet_ntoa(ip->iph_destip));
if (ip->iph_protocol == IPPROTO_ICMP)
printf("protocal: ICMP\n");
else
printf("protocal: Others\n");
struct icmpheader *icmp_pkt = (struct icmpheader *)(packet + sizeof(struct ethheader)
+ sizeof(struct ipheader));
if (ip->iph_protocol == IPPROTO_ICMP) {
char buffer[1500];
memset(buffer, 0, 1500);
/*********************************************************
Step 1: Fill in the ICMP header.
********************************************************/
struct icmpheader *icmp = (struct icmpheader *)
(buffer + sizeof(struct ipheader));
icmp->icmp_type = 0; //ICMP Type: 8 is request, 0 is reply.
icmp->icmp_code = 0;
icmp->icmp_id = icmp_pkt->icmp_id;
icmp->icmp_seq = icmp_pkt->icmp_seq;
printf("icmp id: %d, seq: %d\n", ntohs(icmp_pkt->icmp_id), ntohs(icmp_pkt->icmp_seq));
// Calculate the checksum for integrity
icmp->icmp_chksum = 0;
icmp->icmp_chksum = in_cksum((unsigned short *)icmp,
sizeof(struct icmpheader));
/*********************************************************
Step 2: Fill in the IP header.
********************************************************/
struct ipheader *ipp = (struct ipheader *) buffer;
ipp->iph_ver = 4;
ipp->iph_ihl = 5;
ipp->iph_ttl = 64;
ipp->iph_sourceip.s_addr = ip->iph_destip.s_addr;
ipp->iph_destip.s_addr = ip->iph_sourceip.s_addr;
ipp->iph_protocol = IPPROTO_ICMP;
ipp->iph_len = htons(sizeof(struct ipheader) +
sizeof(struct icmpheader));
printf("send tt source :%s\n", inet_ntoa(ipp->iph_sourceip));
printf("send tt dest: %s\n", inet_ntoa(ipp->iph_destip));
/*********************************************************
Step 3: Finally, send the spoofed packet
********************************************************/
// icmp_pkt->icmp_type = 0;
// icmp_pkt->icmp_code = 0;
// icmp->icmp_chksum = 0;
// icmp->icmp_chksum = in_cksum((unsigned short *)icmp,
// sizeof(struct icmpheader));
send_raw_ip_packet (ipp);
}
}
}
int main()
{
pcap_t *handle;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
char filter_exp[] = "icmp[icmptype]==icmp-echo";
bpf_u_int32 net;
// Step 1: Open live pcap session on NIC with name enp0s3
handle = pcap_open_live("eth0", BUFSIZ, 1, 1000, errbuf);
printf("listening on network card, ret: %p...\n", handle);
// Step 2: Compile filter_exp into BPF psuedo-code
printf("try to compile filter...\n");
pcap_compile(handle, &fp, filter_exp, 0, net);
printf("try to set filter...\n");
pcap_setfilter(handle, &fp);
// Step 3: Capture packets
printf("start to sniff...\n");
pcap_loop(handle, -1, got_packet, NULL);
pcap_close(handle); //Close the handle
return 0;
}
- 我们在一个终端ping 1.1.1.1。
- 在程序运行端可以看到有记录,包括icmp id,序列号,发送端和收端,以及协议为ICMP等信息:
遇到的问题及解决办法
- 在做任务Task2.1及Task2.3时,我发现运行.c文件总是报错:
我刚开始还以为我的libpcap版本安装不太对,重新安了一个版本后发现还是不行。后来我仔细研究代码,发现我的网卡是eth0,不是enp0s3,可能用vbox的网卡名就是enp0s3,我更改代码后就能成功运行了。