Skip to main content
The IDS/IPS engine runs six heuristic detectors in sequence on every captured packet. Each detector targets a distinct attack pattern, uses its own threshold, and calls guardar_ataque() to log, classify, and optionally block the offending IP.

SYN flood

A SYN flood sends a continuous stream of TCP SYN packets to a target without completing the three-way handshake. The server allocates a half-open connection entry for each unanswered SYN. Once the connection table is exhausted, the server can no longer accept legitimate connections. Detector: detectar_syn_flood(packet)
ids.py
def detectar_syn_flood(packet):
    if packet.haslayer(scapy.TCP) and str(packet[scapy.TCP].flags) == 'S':
        ip_src  = packet[scapy.IP].src
        ip_dst  = packet[scapy.IP].dst
        puerto  = packet[scapy.TCP].dport
        t       = time.time()

        # Sliding window: keep only timestamps within the last 500 ms
        paquetes_por_ip[ip_src].append(t)
        paquetes_por_ip[ip_src] = [ts for ts in paquetes_por_ip[ip_src] if t - ts <= 0.5]

        if len(paquetes_por_ip[ip_src]) > THRESHOLD_SYN_FLOOD:
            guardar_ataque(ip_src, "SYN Flood", 'TCP', puerto, ip_dst,
                           flag=str(packet[scapy.TCP].flags))
PropertyValue
ProtocolTCP
Flag filterS only (pure SYN, no ACK)
ThresholdTHRESHOLD_SYN_FLOOD = 10 packets in 500 ms
SeverityALTA
ML classificationYes (if IPS mode is active)
The detector counts packets per source IP inside a 500 ms sliding window. A legitimate browser opening a connection generates one SYN; 10 SYNs from the same IP in half a second is an unambiguous flood signal.

DDoS distribuido

A distributed denial-of-service attack sends a high volume of packets from many source IPs toward a single destination, overwhelming its bandwidth or processing capacity. Because the traffic originates from thousands of hosts, blocking a single IP is insufficient. Detector: detectar_ddos(packet)
ids.py
def detectar_ddos(packet):
    if packet.haslayer(scapy.IP):
        ip_dst    = packet[scapy.IP].dst
        ip_src    = packet[scapy.IP].src
        puerto    = packet[scapy.TCP].dport if packet.haslayer(scapy.TCP) else 0
        protocolo = 'TCP' if packet.haslayer(scapy.TCP) else 'UDP'

        if protocolo == 'UDP' and puerto == 0:
            return

        t = time.time()
        paquetes_por_ip[ip_dst].append(t)
        # Count ALL traffic arriving at the destination in the last 1 second
        paquetes_por_ip[ip_dst] = [ts for ts in paquetes_por_ip[ip_dst] if t - ts <= 1]

        if len(paquetes_por_ip[ip_dst]) > THRESHOLD_DDOS:
            guardar_ataque(ip_src, "DDoS Distribuido", protocolo, puerto, ip_dst)
PropertyValue
ProtocolTCP or UDP
Counting keyDestination IP (not source)
ThresholdTHRESHOLD_DDOS = 500 packets in 1 s
SeverityALTA
ML classificationYes
Unlike the other detectors, detectar_ddos indexes paquetes_por_ip by destination IP. This lets it aggregate traffic from many sources and fire when the total inbound rate exceeds the threshold — the defining characteristic of a volumetric DDoS.

Port scan

Port scanning is a reconnaissance technique where an attacker probes a host for open ports and running services. Tools such as nmap send a SYN to each port and record which ones reply. The IDS detects this by tracking how many distinct destination ports a single source IP has contacted. Detector: detectar_escaneo_puertos(packet)
ids.py
def detectar_escaneo_puertos(packet):
    if packet.haslayer(scapy.TCP) and packet[scapy.TCP].flags == 'S':
        ip_src = packet[scapy.IP].src
        ip_dst = packet[scapy.IP].dst
        puerto = packet[scapy.TCP].dport

        # Set automatically deduplicates — only unique ports are counted
        puertos_por_ip[ip_src].add(puerto)

        if len(puertos_por_ip[ip_src]) > PORT_SCAN_THRESHOLD:
            guardar_ataque(ip_src, "Escaneo de Puertos", 'TCP', puerto, ip_dst)
PropertyValue
ProtocolTCP
Flag filterS only
Counting keySource IP → set of unique destination ports
ThresholdPORT_SCAN_THRESHOLD = 10 unique ports (cumulative per session)
SeverityMEDIA
ML classificationYes
Port counting is cumulative for the session — there is no time window. Once puertos_por_ip[ip_src] has grown past the threshold, every subsequent SYN from that IP triggers an alert until monitoring is restarted.

Posible exploit

Exploit detection watches for connection attempts to a fixed list of ports historically associated with critical vulnerabilities. The list covers protocols that have been the entry point for major malware campaigns (WannaCry via SMB, BlueKeep via RDP, and others). Detector: detectar_exploit(packet)
ids.py
def detectar_exploit(packet):
    if not packet.haslayer(scapy.IP):
        return

    ip_src = packet[scapy.IP].src
    # Skip whitelisted IPs and trusted CIDR ranges
    if ip_src in IPS_CONFIABLES or ip_en_rangos(ip_src):
        return

    ip_dst    = packet[scapy.IP].dst
    protocolo = 'TCP' if packet.haslayer(scapy.TCP) else \
                'UDP' if packet.haslayer(scapy.UDP) else 'OTRO'

    if protocolo not in {'TCP', 'UDP'}:
        return

    puerto = packet[scapy.TCP].dport if protocolo == 'TCP' else packet[scapy.UDP].dport
    flag   = str(packet[scapy.TCP].flags) if protocolo == 'TCP' else 'N/A'

    PUERTOS_EXPLOIT = {135, 139, 445, 3389, 5900, 21, 22, 23, 69}

    if puerto in PUERTOS_EXPLOIT:
        # Only alert on connection initiation — ignore established session traffic
        if protocolo == 'TCP' and flag not in ['S', 'SA']:
            return
        guardar_ataque(ip_src, "Posible Exploit", protocolo, puerto, ip_dst, flag=flag)
Monitored ports:
PortProtocolCommon exploit
21FTPBrute force, anonymous login, CVE-2010-4221
22SSHBrute force, credential stuffing
23TelnetCleartext credentials, Mirai botnet
69TFTPUnauthenticated file transfer
135MS-RPCDCOM exploits, CVE-2003-0352
139NetBIOSSMB relay, legacy Windows shares
445SMBEternalBlue (CVE-2017-0144), WannaCry
3389RDPBlueKeep (CVE-2019-0708), brute force
5900VNCAuthentication bypass, CVE-2006-2369
PropertyValue
ProtocolTCP or UDP
Flag filterS (SYN) or SA (SYN-ACK) only
ThresholdNone — any connection to a listed port triggers an alert
SeverityCRITICA
ML classificationYes
This detector fires on any connection attempt from a non-whitelisted IP to these ports, with no packet-count threshold. Cloud provider IPs and private ranges are excluded via the whitelist; see Heuristic detection rules for details.

SQL injection

SQL injection detection works at the application layer by inspecting the raw byte payload of TCP packets. The detector decodes the Raw Scapy layer as UTF-8 and applies a multi-pattern regular expression covering the most common injection techniques. Detector: detectar_sql_injection(packet)
ids.py
def detectar_sql_injection(packet):
    if packet.haslayer(scapy.Raw):
        try:
            ip_src = packet[scapy.IP].src
            if ip_src in IPS_CONFIABLES or ip_en_rangos(ip_src):
                return

            carga = packet[scapy.Raw].load.decode(errors='ignore')

            if not carga.isascii() or len(carga) > 1000:
                return

            # ... exclusion list for benign HTTP params ...

            sql_pattern = re.compile(
                r"(?i)(\b(select|union|insert|update|delete|drop|alter|create|"
                r"exec|execute|cast|declare|grant|revoke)\b"
                r".?(--|#|;|/\|\*/|@@|char\(|nchar\(|varchar\(|nvarchar\()|"  
                r"('(\s)or(\s)\d+=\d+)|"
                r"(\bunion\b.*\bselect\b)|"
                r"(\bexec\b(\s|\+)+(s|x)p\w+)|"
                r"(;?\s*--)|"
                r"(\bwaitfor\b\s+delay\b)|"
                r"(sleep\(\d+\))"
                r")"
            )
            if sql_pattern.search(carga):
                ip_dst = packet[scapy.IP].dst
                guardar_ataque(ip_src, "SQL Injection", "TCP", puerto,
                               ip_dst, usar_ml=False)
        except Exception as e:
            print(f"[X] Error en SQLi: {e}")
Regex patterns matched:
PatternTechnique
SELECT … --, DROP … ;SQL keyword followed by a comment or terminator
' OR 1=1Authentication bypass
UNION SELECTData extraction from additional tables
EXEC sp_* / EXEC xp_*Stored procedure execution (SQL Server)
; --Inline comment to truncate original query
WAITFOR DELAYBlind time-based injection (SQL Server)
sleep(N)Blind time-based injection (MySQL)
PropertyValue
ProtocolTCP (payload-bearing packets only)
ThresholdNone — single pattern match triggers alert
SeverityCRITICA
ML classificationNo (usar_ml=False) — the payload analysis is definitive
ML classification is skipped for SQL injection because the regex match on the raw payload is already high-confidence evidence. Running the ML classifier on top would be redundant and could introduce latency.

UDP flood

A UDP flood saturates a target with high-volume UDP traffic. Because UDP has no handshake, the server must process every datagram (or generate ICMP port-unreachable responses), quickly exhausting bandwidth and CPU. DNS amplification attacks are a common variant. Detector: detectar_udp_flood(packet)
ids.py
def detectar_udp_flood(packet):
    if packet.haslayer(scapy.IP):
        ip_dst    = packet[scapy.IP].dst
        ip_src    = packet[scapy.IP].src
        puerto    = packet[scapy.UDP].dport if packet.haslayer(scapy.UDP) else 0
        protocolo = 'UDP' if packet.haslayer(scapy.UDP) else 'TCP'

        if protocolo == 'TCP' and puerto == 0:
            return

        t = time.time()
        paquetes_por_ip[ip_dst].append(t)
        paquetes_por_ip[ip_dst] = [ts for ts in paquetes_por_ip[ip_dst] if t - ts <= 1]

        if len(paquetes_por_ip[ip_dst]) > THRESHOLD_UDP_FLOOD:
            guardar_ataque(ip_src, "UDP Flood", protocolo, puerto, ip_dst)
PropertyValue
ProtocolUDP (primary); also fires on TCP if port is non-zero
Counting keyDestination IP
ThresholdTHRESHOLD_UDP_FLOOD = 500 packets in 1 s
SeverityALTA
ML classificationYes
detectar_udp_flood and detectar_ddos share the same paquetes_por_ip counter indexed by destination IP and the same 1-second window. On a packet that is both UDP and IP, both detectors evaluate the same count. The UDP flood detector exists to catch UDP-specific saturation patterns that would otherwise be subsumed by the general DDoS detector.

Build docs developers (and LLMs) love