Attacks & threats

7 min readUpdated

What is an ACK flood attack?

Quick answer

An ACK flood is a protocol denial-of-service attack that sends a high rate of TCP packets with the ACK flag set — plain ACK, SYN-ACK or PSH-ACK — that match no established connection. Each looks ordinary alone, so a stateful firewall must look it up in its connection table before discarding it. It exhausts per-packet work and state tables, not bandwidth, so a flood that looks modest on a graph can still fell the device fronting a healthy server.

01What is an ACK flood?

An ACK flood is a stream of TCP packets carrying the acknowledgement (ACK) flag that do not match any connection the target is tracking. In a normal session the ACK flag confirms received data; it is set on almost every packet after the handshake. Attackers abuse that ubiquity: a lone ACK is indistinguishable from legitimate mid-session traffic until something checks whether a matching connection exists.

That check is the whole point. A firewall, load balancer or server that keeps per-connection state must consult its table for every arriving ACK, decide the packet is orphaned, and drop it. At high packet rates that lookup cost — not the raw data volume — is what runs out first.

The family has three common members that share one weakness and differ only in the flags set. They are often mixed inside the same campaign, and frequently layered on top of a SYN flood so the defender is fighting handshake exhaustion and orphaned-packet exhaustion at once.

02ACK, SYN-ACK and PSH-ACK: three variants

All three set ACK. What changes is the extra flag and, with it, the type of bookkeeping the attacker tries to trip.

  • A SYN-ACK flood doubles as a reflection attack: the attacker spoofs the victim's address in SYN packets to many servers, and those servers' SYN-ACK replies all land on the victim
  • A PSH-ACK flood, also called a push flood, carries a payload the PSH flag forces the receiver to process, so it costs slightly more per packet than a bare ACK
  • Real sessions send ACKs constantly, so none of these can be blocked by flag alone without breaking every connection on the box
VariantFlags setWhat it abuses
Plain ACK floodACKConnection-table lookups for packets that match nothing
SYN-ACK floodSYN + ACKLooks like a reply to a SYN the victim never sent — often reflected off third parties
PSH-ACK floodPSH + ACKPSH tells the stack to deliver data up immediately, forcing extra processing per packet
ACK fragmentationACK on IP fragmentsReassembly work before the packet can even be classified

03Why firewalls struggle with it

A purely stateless filter cannot tell an orphaned ACK from a legitimate one, because the only thing that distinguishes them is connection history the filter does not keep. So it either passes everything (useless) or drops ACKs by pattern (breaks real traffic).

A stateful device can tell the difference, but at a price: it has to hold a connection table and look up every packet. Flooding it with orphaned ACKs makes it do that work millions of times a second, and in the SYN-ACK and some ACK cases the packets can even create or churn table entries. The protection mechanism becomes the bottleneck. This is the same trap described for SYN floods, one layer along.

Real incidents show the scale. In April 2024 OVHcloud reported mitigating a record attack of about 840 million packets per second that was 99% TCP ACK flood from roughly 5,000 source addresses, with a 1% DNS reflection component — a packet-rate record driven almost entirely by orphaned ACKs rather than volume.

04How to detect an ACK flood

The signature is a high rate of ACK-bearing packets that do not correlate with your own outbound SYN-ACKs or with established sessions in your connection table. Packet captures make it visible quickly. The filters below were syntax-checked and run against a crafted capture on Debian 12.

  • Packet rate climbs sharply while bytes-per-packet stays small and payloads are usually empty
  • ACKs arrive for destination ports with no listening service, or with sequence numbers that match no session
  • SYN-ACKs arrive that you never requested — the reflection tell
  • Connection-table or firewall CPU saturates well before the uplink does
tcpdump filters for the ACK family (BPF)
# any ACK that is not part of a handshake or teardown
tcpdump -ni eth0 'tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) == 0
                  and tcp[tcpflags] & tcp-ack != 0'

# classic empty-payload ACK flood (ACK set, zero TCP payload bytes)
tcpdump -ni eth0 'tcp[tcpflags] == tcp-ack
   and (ip[2:2] - ((ip[0] & 0x0f) << 2) - ((tcp[12] & 0xf0) >> 2)) == 0'

# PSH-ACK flood
tcpdump -ni eth0 'tcp[tcpflags] & (tcp-push|tcp-ack) == (tcp-push|tcp-ack)
                  and tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) == 0'

# unsolicited SYN-ACK flood (reflection)
tcpdump -ni eth0 'tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)'

05What actually filters it

The durable fix is to decide, cheaply and early, whether a packet belongs to a real connection, and to drop the ones that do not before they reach anything expensive. Connection tracking does exactly this. Depending on its settings, a flagged packet that matches no tracked flow is either marked invalid or shows up as a "new" flow that did not begin with a SYN — and both can be dropped.

The ruleset below was syntax-checked and load-tested in an isolated network namespace. A normal three-way handshake completed through it, while injected stray ACK, PSH-ACK and SYN-ACK packets were all dropped, both with conntrack's mid-stream pickup on (the default) and off. It is a textbook example, not a tuned production policy.

  • Make orphaned-packet handling cheap: drop packets that match no flow as early as possible, before routing, sockets or anything else that costs CPU or allocates state
  • Consider setting `net.netfilter.nf_conntrack_tcp_loose = 0` (the Linux default is 1) so the tracker stops adopting mid-stream flows it never saw open; with pickup on, every orphaned ACK that is not dropped can create a fresh table entry. The trade-off is that established sessions break after a conntrack flush or a firewall failover
  • Rate-shape per source where sources are not spoofed, but expect reflection variants where they are
  • For anything at volume, filter upstream on a network with the packet-per-second headroom to absorb it — a host alone cannot, as explained in how to stop a DDoS attack
Generic conntrack-based drop (nft, verified in a netns)
table inet ack_guard {
    chain prerouting {
        type filter hook prerouting priority -150; policy accept;

        # no matching connection: stray SYN-ACK and RST (stray ACK too if tcp_loose=0)
        ct state invalid counter drop

        # a NEW flow must open with a bare SYN; a 'new' ACK is an orphan
        tcp flags & (fin|syn|rst|ack) != syn ct state new counter drop
    }
}

06What a reverse proxy can and cannot do

For web traffic, a reverse proxy terminates TCP for you. The attacker's orphaned ACKs hit the proxy's network, not your origin, and a platform built for it drops them with the packet-rate capacity the job needs. A reverse proxy such as GuardX absorbs the flood upstream so the origin only ever sees completed, legitimate connections.

The honest limits: this only covers traffic that reaches you through the proxy. If your real server address is exposed, an attacker can send ACKs straight to it and bypass the proxy entirely — so hiding the origin matters, as covered in how to hide your origin IP. And for non-HTTP services the proxy model does not apply; those need routed Layer 3/4 filtering, the model behind GRE transit, which is coming soon. Edge filtering also does nothing about an orphaned-ACK flood aimed at an unprotected address you forgot to firewall.

Questions

Frequently asked questions

It is a protocol attack. It aims to exhaust per-packet processing and connection-table state on stateful devices rather than saturate bandwidth. That is why packet rate matters more than gigabits, and why an ACK flood can knock over a middlebox while the traffic graph still looks ordinary.

Because almost every packet in a real TCP session after the handshake carries the ACK flag. Blocking ACKs by flag alone would break every existing connection on the device. The only safe discrimination is whether a given ACK belongs to a connection the device is already tracking.

Both set the ACK flag and match no real session. A PSH-ACK flood also sets the PUSH flag and usually carries a small payload, which forces the receiving stack to deliver data upward immediately. That adds a little processing per packet, so PSH-ACK floods can be marginally more costly to absorb than bare ACK floods.

Unsolicited SYN-ACKs mean a third party received a SYN with your address spoofed as the source and replied to you. You are the target of a reflected SYN-ACK flood. The sources are innocent servers, so blocking them is pointless; the flood has to be filtered on capacity upstream of your link.

Conceptually yes — it lets a device drop packets that match no flow. In practice the tracker needs enough capacity to classify every packet at attack rate, or it becomes the bottleneck itself. Small floods are manageable on a host; large ones must be filtered upstream where the packet-per-second headroom exists.

Sources

Sources and verification

tcpdump BPF filters validated with tcpdump 4.99.3 against a crafted pcap, and the nftables ruleset syntax-checked and load-tested with nft 1.0.6 in an isolated network namespace, on Debian 12, October 2026.

  1. 01The rise of packet rate attacks: when core routers turn evil (840 Mpps, 99% TCP ACK) · OVHcloud
  2. 02PUSH and ACK floods · Wikipedia
  3. 03RFC 9293: Transmission Control Protocol (TCP) · IETF
  4. 04Connection tracking sysctl variables (nf_conntrack_tcp_loose) · kernel.org
  5. 05nftables Synproxy · nftables wiki

Last updated