python3 raw socket syn半连接扫描

如何使用python完成syn半连接扫描

以前一直使用masscan作为端口探测的神器,但是发现在某些linux(ubuntu)下,masscan可能存在一些问题,比如扫描完成了,等待时间变成负的也不结束,如果单独使用可以接受,但是内置到项目中就比较难受了。还有其他路由问题导致masscan卡住,但是很少遇到。。。。。。

环境

  1. python3
  2. linux

什么是SYN扫描

tcp的三次握手就不多说了,而第一次握手发送的就是syn包,扫描端口发送SYN 数据包,如果能够收到SYN+ACK 数据包,则代表此端口开放,如收到RST 数据包,则证明此端口关闭。构造syn包需要构造ip头部信息和tcp包信息。

构造IP头部信息

ip 头部信息需要源ip和目标ip
根据wireshark抓包构造一个ip头部包
在这里插入图片描述

# 校验和函数
def checksum(msg):
    """TCP 以及 IP校验和"""
    s = 0
    # 每次取2个字节
    for i in range(0, len(msg), 2):
        w = (msg[i] << 8) + (msg[i + 1])
        s = s + w
    s = (s >> 16) + (s & 0xffff)
    s = ~s & 0xffff
    return s


# 构造IP头部信息
hl_version = (4 << 4) + 5  # version 4
diff = 0
total_len = 44
ident = random.randint(18000, 65535)
flags = 0
offset = 0
ttl = 255  #  ttl
protocol = 6  # 协议类型
check = 0  # 校验和,在没校验之前先置0
src_addr = socket.inet_aton("127.0.0.1")  # 源ip
dst_addr = socket.inet_aton("127.0.0.1")  # 目标ip
buffer = struct.pack('!BBHHBBBBH4s4s', hl_version, diff, total_len, ident, flags, offset, ttl, protocol, check,
                     src_addr, dst_addr)
checkSum = checksum(buffer)
_header = struct.pack('!BBHHBBBBH4s4s', hl_version, diff, total_len, ident, flags, offset, ttl, protocol,
                      checkSum, src_addr, dst_addr)

构造SYN TCP头部信息

tcp 头部信息包含源端口和目标端口 同时还有伪头部

在这里插入图片描述

seq = seq
ack_seq = 0
doff = 5
check = 0
offset_res = (doff << 4) + 0
fin, syn, rst, psh, ack, urg = (0, 1, 0, 0, 0, 0) 
tcp_flags = fin + (syn << 1) + (rst << 2) + (psh << 3) + (ack << 4) + (urg << 5)
window = 1200
urg_ptr = 0
tcp_header = struct.pack('!HHLLBBHHH', self.src_port, dst_port, seq, ack_seq, offset_res, tcp_flags, window,
                         check, urg_ptr)
# 伪头部选项 源IP地址(4字节)、目的IP地址(4字节)、协议(2字节)、TCP/UDP包长(2字节)
source_address = socket.inet_aton(self.src_ip)
dest_address = socket.inet_aton(dst_ip)
protocol = 6
tcp_length = len(tcp_header)
psh = struct.pack('!4s4sHH', source_address, dest_address, protocol, tcp_length)
psh = psh + tcp_header
tcp_checksum = checksum(psh)
tcp_header = struct.pack('!HHLLBBHHH', self.src_port, dst_port, seq, ack_seq, offset_res, tcp_flags, window,
                         tcp_checksum, urg_ptr)

完整发送代码

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-

import os
import queue
import random
import socket
import struct
import threading
import time


def checksum(msg):
    """TCP 以及 IP校验和"""
    s = 0
    # 每次取2个字节
    for i in range(0, len(msg), 2):
        w = (msg[i] << 8) + (msg[i + 1])
        s = s + w
    s = (s >> 16) + (s & 0xffff)
    s = ~s & 0xffff
    return s


class SynScan:
    def __init__(self, ip_list, port_list, rate=2000, timeout=5):
        self.ips = ip_list
        self.ports = port_list
        self.timeout = timeout
        self.socket = self._socket()
        self.rate = rate
        self.src_ip = None
        self.finished = False

        if not self.get_local_ip():
            print("请检查网络......")
            os._exit(1)
        self.seq = random.randint(1000000000, 2000000000)

	# 获取本地IP
    def get_local_ip(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            s.connect(("8.8.8.8", 0))
            self.src_ip = s.getsockname()[0]
            return True
        except:
            pass
        return False
	
	# 创建IP包
    def ip_headers(self, src_ip, dst_ip):
        hl_version = (4 << 4) + 5
        diff = 0
        total_len = 40
        ident = random.randint(18000, 65535)
        flags = 0
        offset = 0
        ttl = 255
        protocol = 6
        check = 0
        src_addr = socket.inet_aton(src_ip)
        dst_addr = socket.inet_aton(dst_ip)
        buffer = struct.pack('!BBHHBBBBH4s4s', hl_version, diff, total_len, ident, flags, offset, ttl, protocol, check, src_addr, dst_addr)
        checkSum = checksum(buffer)
        _header = struct.pack('!BBHHBBBBH4s4s', hl_version, diff, total_len, ident, flags, offset, ttl, protocol, checkSum, src_addr, dst_addr)
        return _header
	
	# 创建TCP包
    def tcp_headers(self,  src_ip, dst_ip, src_port, dst_port):
        seq = self.seq
        ack_seq = 0
        doff = 5
        check = 0
        offset_res = (doff << 4) + 0
        fin, syn, rst, psh, ack, urg = (0, 1, 0, 0, 0, 0)
        tcp_flags = fin + (syn << 1) + (rst << 2) + (psh << 3) + (ack << 4) + (urg << 5)
        window = 1024
        urg_ptr = 0
        tcp_header = struct.pack('!HHLLBBHHH', src_port, dst_port, seq, ack_seq, offset_res, tcp_flags, window,  check, urg_ptr)
        # 伪头部选项 源IP地址(4字节)、目的IP地址(4字节)、协议(2字节)、TCP/UDP包长(2字节)
        source_address = socket.inet_aton(src_ip)
        dest_address = socket.inet_aton(dst_ip)
        protocol = 6
        tcp_length = len(tcp_header)
        psh = struct.pack('!4s4sHH', source_address, dest_address, protocol, tcp_length)
        psh = psh + tcp_header
        tcp_checksum = checksum(psh)
        tcp_header = struct.pack('!HHLLBBHHH', src_port, dst_port, seq, ack_seq, offset_res, tcp_flags, window, tcp_checksum, urg_ptr)
        return tcp_header

    def _socket(self):
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)  # raw socket 创建
            s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)  # 可自定义 IP头部信息
            s.settimeout(self.timeout)

            return s
        except:
            # print(e)
            return self._socket()

    def recv_packet(self, src_port):
        while 1:
            try:
                recv_packet, addr = self.socket.recvfrom(1024)
                _ip = addr[0]  # ip 信息
                ack_seq = struct.unpack("!L", recv_packet[28:32])[0]  # 回答的seq
                s_port = struct.unpack("!H", recv_packet[20:22])[0]  # 包内源端口
                d_port = struct.unpack("!H", recv_packet[22:24])[0]  # 包内目的端口
                if src_port == d_port and ack_seq == self.seq + 1:
                    if recv_packet[33] == 18:   #  18 代表回包 syn 和 ack 确认包, 端口开放
                        print(f"地址:{_ip}  开放端口:{s_port}")
            except:
                pass
            if self.finished:
                break

    def start(self):
        src_port = random.randint(30000, 60000)  # 随机一个源端口
        t = threading.Thread(target=self.recv_packet, args=(src_port, ))
        t.start()
        time_sleep = 1 / self.rate
        failed_packet = queue.Queue()
        for ip in self.ips:
            for port in self.ports:
                packet = self.ip_headers(self.src_ip, ip) + self.tcp_headers(self.src_ip, ip, src_port, port)
                try:
                    self.socket.sendto(packet, (ip, 0))  # 因为某些操作系统协议栈问题,可能会失败
                except Exception as e:
                    failed_packet.put((ip, packet))
                time.sleep(time_sleep)

        while not failed_packet.empty():  # 重新发送失败的队列数据
            ip, packet = failed_packet.get()
            try:
                self.socket.sendto(packet, (ip, 0))
            except:
                failed_packet.put((ip, packet))
            time.sleep(time_sleep)

        time.sleep(self.timeout+1)
        self.finished = True
        self.socket.close()


if __name__ == '__main__':

    ip_list = ['172.16.0.' + str(i) for i in range(1, 255)]
    port_list = [135, 139, 445, 3389, 22, 21, 23, 9100, 9200, 7001]
    synScan = SynScan(ip_list, port_list)
    synScan.start()



结果如下

[root@localhost ]# python3 synscan.py 
地址:172.16.0.10  开放端口:135
地址:172.16.0.10  开放端口:139
地址:172.16.0.10  开放端口:445
地址:172.16.0.10  开放端口:21
地址:172.16.0.28  开放端口:135
地址:172.16.0.28  开放端口:139
地址:172.16.0.28  开放端口:445
地址:172.16.0.28  开放端口:3389
地址:172.16.0.31  开放端口:135
地址:172.16.0.31  开放端口:21
地址:172.16.0.38  开放端口:135
地址:172.16.0.38  开放端口:139
地址:172.16.0.38  开放端口:445
地址:172.16.0.47  开放端口:135
地址:172.16.0.47  开放端口:139
地址:172.16.0.47  开放端口:445
地址:172.16.0.53  开放端口:135
地址:172.16.0.53  开放端口:139
地址:172.16.0.53  开放端口:445
地址:172.16.0.54  开放端口:135
地址:172.16.0.54  开放端口:139
地址:172.16.0.54  开放端口:445
地址:172.16.0.54  开放端口:3389
地址:172.16.0.55  开放端口:135
地址:172.16.0.55  开放端口:139
地址:172.16.0.55  开放端口:445
地址:172.16.0.64  开放端口:22
地址:172.16.0.44  开放端口:139
地址:172.16.0.44  开放端口:445
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SYN连接扫描是一种常见的端口扫描方法,攻击者可以利用它来探测目标主机上开放的端口和服务,从而进行后续的攻击。SYN连接扫描的过程如下: 1. 攻击者向目标主机发送SYN包,但是不完整地建立TCP连接,而是发送一个SYN标志位,并等待目标主机的响应。 2. 如果目标主机的端口是关闭的,那么它会发送一个RST包作为响应,攻击者可以根据这个响应判断目标主机上的端口是关闭的。 3. 如果目标主机的端口是开放的,那么它会发送一个SYN/ACK包作为响应,攻击者可以根据这个响应判断目标主机上的端口是开放的。 4. 攻击者在接收到目标主机的响应后,会将连接关闭,以避免留下不必要的痕迹。 SYN连接扫描是一种隐蔽性较强的端口扫描方法,因为它不会完整地建立TCP连接,而只是发送一个SYN包,从而减少了被检测到的风险。为了防范SYN连接扫描,可以采取以下措施: 1. 配置防火墙:配置防火墙可以限制非法流量的访问,减少服务器受到攻击的风险。 2. 使用入侵检测系统:使用入侵检测系统可以监控网络流量,及时发现和防御SYN连接扫描等攻击。 3. 限制TCP连接:限制TCP连接可以减少攻击者使用SYN连接扫描的风险,例如可以限制单个IP地址的TCP连接数。 4. 更新操作系统和应用程序:更新操作系统和应用程序可以修补安全漏洞,增强系统的安全性和可用性,减少受到攻击的风险。 综上所述,SYN连接扫描是一种常见的端口扫描方法,我们应该采取相应的防范措施,增强系统的抗攻击能力,保护系统的安全和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值