环境
关于路由环境的搭建可以参考这篇博客 Linux路由配置实战(不同网段通信)
最终搭建环境如下:
虚拟机 | 说明 |
---|---|
centos7.0 | 路由器 172.16.17.196 + 192.168.43.104 |
windows 7 A | 通信A端 192.168.43.79 |
ubuntu18.04 B1 | 通信B端 172.16.17.199 |
ubuntu18.04 B2 | 通信B端 172.16.17.194 |
SSH链接
1、通信B端设置SSH的服务端
# debian/deepin/ubuntu
sudo apt install openssh-server
# 启动ssh服务
sudo systemctl start sshd
# 设置ssh开机自启动
sudo systemctl enable sshd
2、通信A端链接(利用putty)
链接B2
链接B1
IP欺骗配置
通过hook路由器的netfilter防火墙可以实现,具体代码就不粘了,有想学习的同学可以找我私聊
主要代码如下:
改变daddr:
#include <net/ip.h>
#include <net/tcp.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/init.h>
#include <linux/inet.h> /*in_aton()*/
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/module.h>
#include <linux/socket.h>/*PF_INET*/
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>/*NF_IP_PRE_FIRST*/
#define ETHALEN 14
#define NIPQUAD(addr) \
((unsigned char *)&addr)[0], \
((unsigned char *)&addr)[1], \
((unsigned char *)&addr)[2], \
((unsigned char *)&addr)[3]
__be32 sip,dip;
static struct nf_hook_ops nfho1, nfho2;
char tmp_ip[20];//存储初始的目的IP地址
unsigned int tcpsend(unsigned int hooknum,
struct sk_buff *__skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct sk_buff *skb;
struct net_device *dev;
struct iphdr *iph;
struct tcphdr *tcph;
int tot_len;
int iph_len;
int tcph_len;
skb = __skb;
if (skb == NULL)
return NF_ACCEPT;
iph = ip_hdr(skb);
if (iph == NULL)
return NF_ACCEPT;
tot_len = ntohs(iph->tot_len);
if (iph->daddr == in_aton("172.16.17.199"))
{
iph_len = ip_hdrlen(skb);/*in ip.h*/
//skb_pull(skb, iph_len); //skb->data指针定位到了传输层
//skb_reset_transport_header(skb);//重置首部长度,现在的首部长度包括了的ip首部长度
if (iph->protocol == IPPROTO_TCP)
{
printk("daddr change to 172.16.17.194\n");
iph->daddr = in_aton("172.16.17.194");
tcph = tcp_hdr(skb);
tcph_len = tcp_hdrlen(skb);
dev = dev_get_by_name(&init_net, "ens33");
tcph->check = 0;
skb->csum = csum_partial((unsigned char *)tcph, tot_len - iph_len, 0);
tcph->check = csum_tcpudp_magic(iph->saddr,
iph->daddr,
ntohs(iph->tot_len) - iph_len, iph->protocol,
skb->csum);
iph->check = 0;
iph->check = ip_fast_csum(iph, iph->ihl);
skb->ip_summed = CHECKSUM_NONE;
skb->pkt_type = PACKET_HOST;
skb->dev = dev;
//skb_reset_transport_header(skb);
//skb_push(skb, iph_len); //在返回之前,先将skb中得信息恢复至原始L3层状态
//skb_reset_transport_header(skb);
//skb_push(skb, ETHALEN);
//skb_reset_transport_header(skb);
}
}
return NF_ACCEPT;
}
static int __init filter_init(void)//注册发送的数据包
{
int ret;
nfho1.hook = tcpsend;
nfho1.pf = AF_INET;
nfho1.hooknum = NF_INET_POST_ROUTING;
nfho1.priority = NF_IP_PRI_FIRST;
ret = nf_register_hook(&nfho1);
if (ret < 0)
{
printk("%s\n", "can't modify skb hook!");
return ret;
}
return 0;
}
static void filter_fini(void)
{
nf_unregister_hook(&nfho1);
}
module_init(filter_init);
module_exit(filter_fini);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("druid");
改变saddr:
#include <net/ip.h>
#include <net/tcp.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/init.h>
#include <linux/inet.h> /*in_aton()*/
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/module.h>
#include <linux/socket.h>/*PF_INET*/
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>/*NF_IP_PRE_FIRST*/
#define ETHALEN 14
#define NIPQUAD(addr) \
((unsigned char *)&addr)[0], \
((unsigned char *)&addr)[1], \
((unsigned char *)&addr)[2], \
((unsigned char *)&addr)[3]
__be32 sip,dip;
static struct nf_hook_ops nfho2;
char tmp_ip[20];//存储初始的目的IP地址
unsigned int tcprecv(unsigned int hooknum,
struct sk_buff *__skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct sk_buff *skb;
struct net_device *dev;
struct iphdr *iph;
struct tcphdr *tcph;
int tot_len;
int iph_len;
int tcph_len;
skb = __skb;
if (skb == NULL)
return NF_ACCEPT;
iph = ip_hdr(skb);
if (iph == NULL)
return NF_ACCEPT;
tot_len = ntohs(iph->tot_len);
if (iph->saddr == in_aton("172.16.17.194"))
{
iph_len = ip_hdrlen(skb);/*in ip.h*/
//skb_pull(skb, iph_len); //skb->data指针定位到了传输层
//skb_reset_transport_header(skb);//重置首部长度,现在的首部长度包括了的ip首部长度
if (iph->protocol == IPPROTO_TCP)
{
printk("saddr change to 172.16.17.199\n");
iph->saddr = in_aton("172.16.17.199");
tcph = tcp_hdr(skb);
tcph_len = tcp_hdrlen(skb);
dev = dev_get_by_name(&init_net, "ens33");
tcph->check = 0;
skb->csum = csum_partial((unsigned char *)tcph, tot_len - iph_len, 0);
tcph->check = csum_tcpudp_magic(iph->saddr,
iph->daddr,
ntohs(iph->tot_len) - iph_len, iph->protocol,
skb->csum);
iph->check = 0;
iph->check = ip_fast_csum(iph, iph->ihl);
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->pkt_type = PACKET_HOST;
skb->dev = dev;
//skb_reset_transport_header(skb);
//skb_push(skb, iph_len); //在返回之前,先将skb中得信息恢复至原始L3层状态
//skb_push(skb, ETHALEN);
//skb_reset_transport_header(skb);
}
}
return NF_ACCEPT;
}
static int __init filter_init(void)//注册发送的数据包
{
int ret;
nfho2.hook = tcprecv;
nfho2.pf = AF_INET;
nfho2.hooknum = NF_INET_PRE_ROUTING;
nfho2.priority = NF_IP_PRI_FIRST;
ret = nf_register_hook(&nfho2);
if (ret < 0)
{
printk("%s\n", "can't modify skb hook!");
return ret;
}
return 0;
}
static void filter_fini(void)
{
nf_unregister_hook(&nfho2);
}
module_init(filter_init);
module_exit(filter_fini);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("druid");
改变daddr:
注册在ens33网卡(172.16.17.104)的NF_INET_POST_ROUTING上,
改变saddr:
注册在ens33网卡(172.16.17.104)的NF_INET_PRE_ROUTING上,
为甚么要这样做呢,因为这样可以使得网卡内部的源和目的IP地址相同,不然会出错。原理如下:
这样 内部通信都是199
但是ens33网卡却在和194通信
测试
socket尝试
成功
SSH尝试
成功
错误样例
错误一
从43.79的虚拟机抓包发现,主机会发送回一个RST包
原因简单,就是发过去的是目的地址是17.198,但是回来的地址却是17.194,说明没有修改好。
但是从宿主机抓包看,却发现saddr与daddr都是194
我认为,应该是43.79的包是在虚拟网卡内部抓的,宿主机的包在虚拟网卡外部抓的。所以虚拟网卡没有改好IP地址的时候,被43.79抓到,改好IP地址后,被宿主机抓到
改正hooknum与priority设置,导致错误二:
错误二
TCP链接建立成功,但是发送一个包就会出错,有时候建立的时候会出错,有时候发送多个包后会出错,有时候发送keep-alive包会出错
发现是因为包分段了,分段包会错误
于是卸载netfilter的驱动,进行普通的TCP链接,发现
也就是43.79只显示发送了SYN
宿主机抓包显示SYN———SYN+ACK————RST,断定是中间的路由出现问题
重新配置下路由,果然,之后socket各种发包无问题。(是因为隔了几天,重启了下虚拟机,路由需要重新配置下)
再开启netfilter驱动进行IP欺骗,进行socket链接,但是还是会出错,导致错误三
错误三
我打印路由的包可以发现,包过去了,但是没有收回
禁止发送RST包
但是没用想了下,是不是hook的时机不同导致的?比如,先是PRE再是POST还是网卡绑定有问题?经过长时间遍历,确定问题网卡名字与HOOK时机上,于是有了以下思路
导致错误四
错误四
tcpdump只能识别出IP包,连SYN包都无法识别,经过反复查证,在ubuntu下载wireshark抓包查看后,终于明白问题。
问题出在netfilter对数据的改动问题:
//skb_pull(skb, …);
//skb_push(skb, …);
//skb_reset_transport_header(skb)
以上函数我没正确的使用,于是造成了数据包无法识别问题,全部注释掉后,成功实现ssh欺骗链接,进入错误五
错误五
SSH链接总时定期断掉,为调试增添麻烦:
改正:
sudo vi /etc/ssh/sshd_config
60s发一个心跳包,3次不回答后断掉