There is a feature on your firewall that reads the inside of your packets, finds an IP address and a port number written in the text, and opens an inbound hole for them. No rule. No change request. No log entry you would ever look at. It is on by default on most kit that has it, and has been for the best part of twenty-five years, while the industry that put it there spent the last twenty quietly concluding that it should be switched off — in standards documents, in kernel defaults and in four separate rounds of emergency browser patches.
It is called a protocol helper, or an Application Level Gateway, an ALG, a session helper, a fixup, an inspection engine, a conntrack helper. Same thing. It exists because NAT broke a handful of protocols that write addresses into their own payload, and somebody decided the least-bad fix was to teach the translator to read and rewrite that payload in flight.
Here is the part that should stop you.
The helper cannot tell who wrote the text. It reads bytes off a connection and acts on them, immediately, without asking anything or anyone. It has no way to know whether the string PORT 192,168,1,29,4,0 was produced by a real FTP client doing a real transfer, or by a hidden form on a web page that your user opened by accident. Both look identical on the wire, because they are identical on the wire. So a stranger on the internet who can persuade anything on your network to emit the right bytes — a browser, a chat client, anything that will send what it is told — gets to pick which inbound port your firewall opens, and to what.
That is not a bug in one vendor’s parser. It is what the feature does: a security device taking configuration instructions from untrusted data and applying them immediately. The bug reports and the CVEs are detail on top of that shape.
Samy Kamkar demonstrated the browser version in January 20101, a far better one in October 20202, and three months later Armis extended it so the port opened need not even be on the machine that clicked — it can be on your printer, your camera, or a logic controller two VLANs over3. In between, the IETF wrote down that these should be off by default4, Linux turned them off in the kernel5, and the browser vendors shipped a list of blocked ports that reads almost line for line like a directory listing of Linux conntrack modules6. Every one of those is a fix applied somewhere else, because the thing that actually needed fixing sits in a box nobody is going to patch.
The conclusion I have come to is that protocol helpers should be off by default everywhere, and off in fact on every network you are responsible for. Not tuned. Not restricted to trusted subnets as a first resort. Off, with the two or three genuine exceptions written down and dated, exactly as you would document any other inbound rule — because that is what they are.
What follows is the mechanism, the attacks in the order they were found, the compliance position — in the UK this is a plain failure of a control you may already hold — and the commands to put it right.
What A Stranger Gets Out Of It
Start with the outcome, because the mechanism is easier to care about once you have seen the bill. Somebody opens a link. That is their whole contribution. The page runs JavaScript that talks to the attacker’s server, and that server shapes the conversation — padding it, measuring it, acknowledging parts of it and not others — until one segment arrives at your firewall looking exactly like the opening message of a VoIP call. Your firewall believes it, because believing it is the feature. It creates an inbound mapping, and the attacker connects straight back through.
In the 2020 version the port opens on the machine that clicked, so every service bound on that host is reachable from the internet for as long as the mapping lives2. File sharing. The remote desktop nobody meant to leave listening. On the Fisher-Price OS (Windows), Armis made the obvious point: reach the file-sharing port and you are one unpatched host from the path WannaCry took3.
The 2021 version is worse, and it is the part that should decide it for you. Because the H.323 helper handles call forwarding, one message can name a third address rather than either end of the connection, so the attacker is no longer limited to the machine that clicked and can walk your internal range instead, opening a port on each address and reading back whatever answers. Armis demonstrated exactly that: port 80 across a range, banners collected, a target chosen, then the printer’s raw print port opened and a job sent to it. Their second demonstration reached a logic controller on its unauthenticated management port and rewrote its programme3.
No vulnerability was exploited on any of those devices. They were all working exactly as intended, and the only thing that failed was the boundary — which failed by doing precisely what it was configured to do. Consider who is inside that boundary, too. Not just your staff: a visitor on the guest network, a contractor’s laptop, anyone with a browser. The attack needs no credentials, no foothold and no malware, because the browser is the delivery mechanism and it is already installed and already trusted.
A firewall is for making sure nothing outside starts a conversation with anything inside unless you said so. The helper is the exception, granted on the strength of a string in a packet.
What A Protocol Helper Actually Is
A plain NAT is a dumb, honest thing. A packet arrives, it rewrites the source address and port in the header, writes a row so the reply can be turned back round, and forwards it. It never looks below the transport header, and does not know or care whether the bytes inside are a web request, a database query or a photograph of a dog.
That works until a protocol writes an address into its own payload. FTP does it, in plain text in the command stream: connect back to me at this address, on this port. SIP does it in the Via, Contact and SDP fields, H.323 does it, and IRC does it for direct transfers. All were designed when a host’s address was its address and every machine could reach every other, and under that assumption it is sensible. Put a translator in the path and the address in the payload becomes a lie.
So the helper was invented. It reads the payload, finds the address, rewrites it to the public one, and then does the bit everybody skips over: it creates a rule permitting the inbound connection that the payload just described. Netfilter calls that rule an expectation. Cisco calls it a pinhole, or a NAT door7. Palo Alto calls it a dynamic NAT pinhole8. Juniper calls it a gate. Different word, identical object: a hole punched in the boundary, on the strength of something read out of a packet.
The IETF named the pattern before most of these products existed. An ALG, RFC 2663 says, is an “application specific translation agent” that “may interact with NAT to set up state […] modify application specific payload and perform whatever else is necessary to get the application running across disparate address realms”9. Read that with an adversary in mind: whatever else is necessary, driven by application specific payload. And by February 2002 the IETF’s middlebox taxonomy was calling the mechanism what it is, noting that some ALGs create fragmentation problems “although in this case the problem is arguably the result of a deliberate layer violation (e.g., mucking with the application data stream of an FTP control connection by twiddling TCP segments on the fly)”10.
A deliberate layer violation. Twiddling TCP segments on the fly. That is the mechanism, named by the people who catalogued it twenty-four years ago, and every attack here walks straight through it.
The Expectation Is The Whole Problem
An expectation is a pre-authorised connection: a tuple of source address, source port, destination address, destination port and protocol, some fields filled in and some left as wildcards, plus a timer. When a matching packet arrives, the firewall treats it as related to an existing permitted flow rather than as a new inbound connection, lets it through, and consumes the expectation. On a healthy system the list of them is empty, which is the point — expectations are meant to be rare, short-lived and driven by something an inside host genuinely asked for.
Two of those answers are worse than people expect. The values come from the payload rather than from the firewall, so they come from whichever end of the conversation the helper happened to be reading. And on the IRC helper the source address is a wildcard by design, because the protocol cannot know who will connect: it “creates expectations whose destination address is the client address and source address is any address”11.
Here is the sentence to carry forward. An expectation is an inbound firewall rule, created at line rate, by an untrusted party, with no record of who asked for it or why. Hold it until the Cyber Essentials section, because it is the whole of the argument there.
As Designed: FTP Says PORT, And The Firewall Believes It
Take the simplest helper and watch it work correctly, because the attack is the same sequence with one participant swapped out. Active FTP uses two connections. The client opens a control connection to the server on port 21 and gives it commands in plain ASCII, and when a transfer is wanted it opens a listening socket and sends a PORT command naming the address and port to connect back to, at which point the server connects inbound. That is the protocol as specified, and it has worked that way since 1985. On the wire it is six numbers, four for the address and two for the port, high byte first:
PORT 192,168,1,29,4,0
That is 192.168.1.29, port 1024, because 4 × 256 + 0 = 1024.
The helper watches that stream, spots PORT, rewrites the address from the private one to the public one while adjusting the sequence numbers because the string changed length, and creates an expectation permitting the server’s inbound connection on the port named. Genuinely useful, entirely reasonable given the constraints of 1994, and correct at every step.
Nowt else got checked, because there is nowt else to check — FTP has no signature to offer, no session key and no authentication of any kind, and the helper is reading a stream it is not a party to.
One more thing lives in that code, and it is a warning the authors wrote to themselves. If the address in the PORT command is not the client’s own — if the client asks the server to connect somewhere else entirely — the Linux helper refuses by default, and the comment in the source says why: “DMZ machines opening holes to internal networks, or the packet filter itself”12. Set the loose module parameter and that refusal goes away. The people who wrote the helper knew exactly what it could be made to do. They shipped the safe default and a switch, and twenty-five years later the switch is still there.
Not As Designed: The Same Bytes, From A Web Page
A helper reads a byte stream and matches a pattern. It does not, and structurally cannot, verify that the thing at the other end is the client it is pretending to be. Samy Kamkar published the consequence in January 2010 and called it NAT Pinning1. The mechanic is embarrassingly small: put a form on a web page, point it at the attacker’s server on port 6667, and arrange the body so that it contains a direct-chat request.
PRIVMSG samy :^ADCC CHAT samy 3325256705 22^A
The browser submits it, believing it is doing an HTTP POST. The router’s IRC helper, watching a connection on port 6667, sees DCC CHAT go past with an address and a port and does what it is built to do. The address there is 198.51.100.1 written as a single decimal number, which is how the protocol encodes it, and the port is 22; nothing in that string was chosen by the victim. The FTP variant is the same idea aimed at port 21, using a passive-mode response line instead1.
No cross-site scripting. No request forgery in the usual sense. No vulnerability in the browser. The browser did what browsers do, the firewall did what it was configured to do, and the result is a port forward to the attacker.
That was 2010. Sixteen years ago. The browser vendors added the IRC ports to their blocked list, which closed that particular door and left the room it opened onto exactly as it was.
Be blunt about the point, because it gets lost under the vendor names and the CVE numbers. The attacks do not exploit a flaw in the helpers. They use the helpers correctly. Every packet is well formed and every check passes honestly. The feature is doing its job, and its job is the problem.
Lining The Bytes Up Where The Helper Will Read Them
There was a real obstacle between NAT Pinning and something much worse, and the way round it is the cleverest thing here. Most helpers will not match a pattern anywhere in a stream: they check that the keyword sits at the start of the data portion of a packet, which is what a real protocol message looks like and what an HTTP body never is, because a body arrives after a pile of headers the attacker does not control. Samy quotes the kernel’s own behaviour — the handler bails unless the method occurs at the start of the data2.
So the attacker needs the browser to emit a segment whose very first byte is the keyword. They cannot write the headers, but they can write the body and make it as long as they like, which turns the problem into arithmetic.
The browser sends a large request with a recognisable delimiter buried in the body; the attacker’s server sniffs it, measures how many bytes of headers came first, and now knows the offset. It then either advertises a segment size that puts the desired byte on a boundary, or sends a partial acknowledgement so the victim retransmits starting exactly there — and Armis adds that the TCP window can be crafted in those acknowledgements too, “in order to fully control how the TCP stream is to be segmented”3. That is the whole trick, and it uses nothing but the far end of a connection the victim’s own browser opened.
Once you have it, the browser is a packet generator pointed at the inside of your firewall. It only has to put the right thirty bytes at the start of a segment.
The 2021 work skipped most of the arithmetic. A browser relay connection over TCP carries an attacker-controlled username field, sent early, which accepts newlines and null bytes — Armis showed a capture where that field is \r\nPORT 192,168,1,29,4,0\r\n3. Worse, that path did not consult the browser’s blocked-port list at all, so the one mitigation the industry had shipped twice was simply bypassed.
NAT Slipstreaming, End To End
Put the pieces together and this is the whole attack, in order.
Total elapsed time: seconds. User interaction: one click. Vulnerabilities exploited on the victim’s machine: none.
The disclosure timeline is evidence in its own right. Samy published on 31 October 2020, and between 6 January and 26 February 2021 Chrome, Edge, Safari and Firefox all shipped mitigations3, tracked as CVE-2020-16043, CVE-2021-23961 and CVE-2021-1799.
Four browser vendors shipped emergency patches for a firewall feature. Armis explain why in their own words: “While the underlying issue of this attack is the way NATs are implemented (in various ways in routers and firewalls, throughout numerous vendors and applications), the easiest and fastest way to mitigate was through a patch to browsers”3.
That is not a fix. It is four other industries putting sandbags round somebody else’s door, because the door itself was never going to be replaced.
H.323 Call Forwarding Points At Anything On Your Network
Most helpers bound the damage without meaning to. The FTP helper pins the expectation’s destination to the client that created it, so the worst an attacker gets is a port on the machine that clicked — bad, but survivable.
H.323 is catastrophic, and the reason is a telephony feature.
A phone system has to support call forwarding, and a forwarded call is by definition a call to somebody not on the line. So the signalling has to name a third endpoint, and the helper has to open a path to it, or forwarding does not work through NAT at all. Every serious implementation supports it, and the netfilter helper documents the behaviour explicitly, with a diagram13. Armis read the code and stated the consequence in one sentence: “a single H.323 packet sent over TCP port 1720 that initiates call forwarding can open a pinhole (named an expectation in the conntrack subsystem) to any TCP port of any internal IP on the network”3.
Any TCP port. Any internal address. From one packet, which the attacker got a browser to send.
So the attack stops being about the victim’s machine and becomes a port scan of your internal network, run from the internet, with the results read back over connections your own firewall is authorising. The devices it finds are the point, because they are not patched, frequently not patchable, and the entire security model for most of them is it is on the inside. Armis put a number on that: a year after publication, 97% of industrial controllers vulnerable to one set of critical flaws were still unpatched3. That is the reality “on the inside” is holding up.
So H.323 is the one to deal with first, on every platform, because it reaches past the victim — and it is also the one almost nobody uses, which makes it the cheapest security improvement here. Turn it off. Nobody will ring you.
The Helper Can Be Fired By A Message You Did Not Send
One variant deserves its own section, because it breaks the assumption people reach for when they want a reason not to act: fine, but the trigger has to come from inside, so control the browsers and you control the problem.
No.
In July 2022 David Leadbeater found two faults in the Linux IRC helper14. The module looks for the string \1DCC anywhere in the stream rather than checking it is a properly framed message in the right place, and its address check compares against the chat server’s address rather than the host behind the translator, so the publicly known address of a public server satisfies it. Put those together and an attacker sends the victim’s client a client-to-client ping — an entirely ordinary thing to receive from any other user — with a direct-transfer request inside it:
PRIVMSG ExampleUser :^APING ^ADCC CHAT x 3325256705 22^A
By the rules of the protocol, the client replies to a ping by echoing the payload back, so the victim’s client dutifully sends that string outbound, and the helper, watching the outbound stream, finds DCC in it and opens the port.
Nobody inside did anything wrong and nobody clicked. The client followed the specification, and the specification and the helper together produced an inbound hole to port 22 on a machine inside the network. It became CVE-2022-2663, and the national vulnerability database description is admirably plain: “A firewall may be able to be bypassed when users are using unencrypted IRC with nf_conntrack_irc configured”15.
Note the word unencrypted. Hold that one too.
The author’s own recommendation is where the rest of this post arrives from a different direction: “Potentially entirely deprecate and remove nf_conntrack_irc, it’s unclear it has much use anymore”14.
The Parsers Are The Other Half Of It
Everything so far has been about helpers working correctly. There is a second, separate problem: they are protocol parsers written in C, in the fast path of a security device, on data supplied by strangers, with the bug rate you would expect. Nor is it a Linux problem or a cheap-router problem — it shows up in every vendor, in the code they charge the most for.
| CVE | Component | What a crafted packet does |
|---|---|---|
| CVE-2018-0051 | Junos SIP ALG | Crashes the flow daemon on SRX and MX; also records that SIP ALG is on by default except on high-end models |
| CVE-2018-15454 | Cisco ASA / FTD SIP inspection | Reloads the device or pins the CPU |
| CVE-2022-2663 | Linux nf_conntrack_irc | Opens ports through the firewall, as above |
| CVE-2023-22412 | Junos SIP ALG | “Specific SIP messages” crash the flow daemon, repeatably |
| CVE-2023-22415 | Junos H.323 ALG | Out-of-bounds write from “specific H.323 packets” |
| CVE-2024-21616 | Junos SIP ALG | One SIP packet exhausts the NAT pool, so genuine traffic stops translating |
| CVE-2024-26851 | Linux nf_conntrack_h323 | Out-of-range bit shift decoding the H.323 bitmap |
| CVE-2024-39551 | Junos H.323 ALG | Memory exhausted by “specific packets” until traffic stops |
Every one is reachable from the network with no authentication, by anyone who can get a packet to the outside interface, which is everyone. And read the wording: specific SIP messages, specific H.323 packets, a specific SIP packet. That is not a protocol failing under load. It is somebody building a packet on purpose.
Sit with the Cisco case. CVE-2018-15454 was published on 31 October 2018 at 8.6 severity, it was being exploited in the wild, and the advisory said the software update was not available yet16. Cisco’s mitigation, in their own advisory, is no inspect sip. So the vendor’s answer to an actively exploited flaw in the feature was to turn the feature off — which raises the question the rest of this post is built on. If turning it off is an acceptable answer during an incident, on what basis is it on the rest of the time?
A Helper Only Works If You Do Not Encrypt
This part should end the argument on its own, and gets the least attention. A protocol helper reads your payload and cannot read an encrypted one. So a helper is only doing anything at all on traffic you have chosen to leave in the clear, and keeping the helper working means keeping that traffic in the clear.
Every protocol on the list has had an encrypted mode for well over a decade. FTP has had TLS since 200517; turn it on and the PORT and PASV exchanges are invisible and the helper does nothing. SIP has had TLS since the base specification, with encrypted media alongside it. H.323 has its own security annexe. Chat has had TLS for a very long time, and the official advice for CVE-2022-2663 was, in as many words, to use it so the helper cannot see your transfer requests15.
So the honest form of “we need the SIP ALG” is: we need our call signalling to travel in plain text across untrusted networks, so a middlebox we do not control can rewrite it. Say it that way in a design review and see how far it gets.
There is a sharper version, and it is why this is not a close call. Leaving a helper enabled is a standing incentive against encryption: the day somebody turns on SIP over TLS, calls break and the helper is why, so the change is backed out and the cleartext stays another year. Ask anyone who has tried it behind a consumer-grade firewall how that went.
Every other protocol on the internet has gone the other way: web traffic encrypted by default, DNS encrypted, mail transport encrypted, QUIC encrypting the transport header itself so middleboxes cannot read it. The middlebox era ended on the open internet years ago, and the last places relying on a device in the path reading the payload are the ones where somebody left a helper on.
IPsec Is The One Where The Helper Cannot Read Anything
Which raises the obvious question about the protocol that is nothing but encryption. The answer is worse than you would guess.
ESP has no port numbers, being an IP protocol in its own right, and a translator demultiplexes return traffic by port. So with two clients behind one address heading for the same gateway there is nothing to tell their inbound packets apart. RFC 3715 wrote that down in March 2004: a NAT cannot learn the mapping by inspection, and “it is possible that the NAT will deliver the incoming IPsec packets to the wrong destination”18.
So vendors built a helper. It watches the IKE exchange on UDP 500, whose early packets are in the clear, harvests the cookies and the security parameter index, and opens a gate so inbound ESP carrying that value reaches the right host inside. Juniper describes it plainly: “When ESP traffic hits the IKE ALG gates, sessions are created to capture subsequent ESP traffic”19. Cisco’s inspect ipsec-pass-thru does the same for ESP and AH “associated with an IKE UDP port 500 connection”, with a default map that sets no limit at all on ESP connections per client20.
Same object, same authority, except the helper is not even matching a keyword. It cannot parse ESP, because ESP is the encrypted part; it is steering packets on a 32-bit number it watched go past in cleartext. The section of RFC 3715 that covers this is titled, without irony, “Helper Incompatibilities”, and records that cookie demultiplexing “results in problems with re-keying” and that devices parsing ISAKMP payloads “may not handle all payload ordering combinations”18. A guess standing in for a rule, and a hand-rolled parser in the packet path, written down twenty-two years ago.
The fix shipped ten months later, in the protocol: RFC 3947 has the two ends detect a translator during the key exchange, and RFC 3948 wraps ESP in UDP 4500 so there are ports again21. Juniper then says the quiet part out loud — “IKE NAT-T traffic on floating port 4500 is not processed in an IKE ALG”19. Do it properly and the helper is bypassed entirely, which is the same sentence as passive FTP and as ICE.
Mainline Linux never took this one. There is no ESP module among the conntrack protocols, and a 2021 patch adding SPI-based tracking went through review on the netfilter list and was never merged22. The vendors who ship it ship it out of tree, on the boxes least likely to be updated.
It Fails Cyber Essentials, Line By Line
Up to here this has been a security argument. For anyone certifying in the United Kingdom it is also a compliance one, and there is no clever interpretation involved — it is three bullet points against three bullet points. Cyber Essentials is the UK government-backed scheme delivered through IASME, its first technical control is firewalls, and the current requirements document is version 3.3, April 2026. Here is what it obliges you to do, verbatim23:
- block unauthenticated inbound connections by default
- ensure inbound firewall rules are approved and documented by an authorised person, and include the business need in the documentation
- remove or disable unnecessary firewall rules, when they are no longer needed
An expectation exists precisely to permit an inbound connection that would otherwise be blocked, and the party whose data caused it authenticated to nothing, so the first bullet fails outright. The rule was written at line rate by a kernel module, so there is no document, no business need recorded and no authorised individual anywhere in the chain — ask for the approval record for the rule that let a connection reach port 9100 on your printer and you have none, and cannot make one, because it existed for ninety seconds eighteen months ago. And a helper’s rules are removed by a timer, which is not a review.
Three requirements, three failures, on the first control of five, applying in the scheme’s own words to “boundary firewalls, desktop computers, laptops, routers, servers”23, which is to say everything you own.
Be fair about what that means, because I am not the certification body. An assessor works from the question set and the evidence you give them, and that set asks whether you block unauthenticated inbound connections by default and whether your inbound rules are documented and approved. Answer yes with a helper enabled and the answer is not true. You may well pass anyway. Passing and complying are not the same thing, and the gap surfaces after an incident.
It is only unusually plainly worded, too: a card-industry standard, a customer’s questionnaire and your insurer’s proposal form all ask the same thing in other words. So this is the section to take to whoever signs the certificate. Not the attacks and not the CVEs. Three bullets, and the honest answer to each.
The Industry Already Decided This, Twenty Years Ago
None of this is new or contested. What is remarkable is how long the decision has been made while the defaults carried on regardless.
RFC 3027 catalogued every protocol NAT breaks in January 200124. RFC 3234 put ALGs in the middlebox taxonomy a year later and was blunt about the cost of adding boxes to a path: it “creates extra points of attack, reduces or eliminates the ability to perform end to end encryption, and complicates trust models”10. Then, in January 2007, RFC 4787 — a Best Current Practice rather than a suggestion — set out how NAT is required to behave, and requirement ten said this:
REQ-10: To eliminate interference with UNSAF NAT traversal mechanisms and allow integrity protection of UDP communications, NAT ALGs for UDP-based protocols SHOULD be turned off.4
Off. Nineteen years ago, with the reason given: helpers get in the way of the mechanisms that actually work, and they stop you protecting your own traffic. The same section notes, wearily, that some products have ALGs “turned on permanently”4.
Three years later NAT Pinning showed a web page opening a port1. Netfilter answered in 2012 with the CT target, which attaches a helper to a named flow by an explicit rule, and a switch to stop automatic assignment altogether11. Then on 25 April 2016 the kernel changed its default, in a commit message worth reading in full for how tired it sounds:
Four years ago we introduced a new sysctl knob to disable automatic helper assignment […] This knob kept this behaviour enabled by default to remain conservative. This measure was introduced to provide a secure way to configure iptables and connection tracking helpers through explicit rules. Give the time we have waited for this, let’s turn off this by default now, worse case users still have a chance to recover the former behaviour by explicitly enabling this back through sysctl.5
That shipped in Linux 4.7, and ever since, a box with those modules loaded does nothing with them until you write a rule attaching one to a flow. Everything after it is in the diagram above, down to the web platform standard itself gaining the port list25.
Now look at the list for what is missing. In twenty-five years not one row of it is a firewall vendor pushing a firmware update that turns these off on kit already deployed. The standards body asked. The kernel did it upstream. The browsers paid for it. The boxes carried on.
And the blocked-port list is the tell. Ports 69, 137, 161, 554, 1719, 1720, 1723, 5060, 5061, 6566 and 10080 are all on it6 — TFTP, NetBIOS, SNMP, RTSP, H.323 twice, PPTP, SIP twice, the scanner protocol and the Amanda backup protocol. List the helper modules in a Linux kernel tree and you will find you have read the same list twice. Not a coincidence, and not a security control: it is one industry maintaining a permanent, growing denylist of ports because another will not change a default.
Under Most Of The Badges, It Is Linux
The netfilter detail is the important part, not a Linux-shaped digression. A very large share of the boxes doing NAT on this planet are Linux with netfilter underneath a vendor interface: every OpenWrt derivative, which is most of the consumer and small-business router market, most ISP-supplied home routers, a good deal of carrier-grade NAT kit, and plenty of commercial appliances whose web interface gives no hint of what is beneath it. The helper parsing your SIP is very often the same nf_conntrack_sip.c that ships in the mainline kernel, compiled by somebody else and driven by a menu.
The evidence is in the research itself. When Samy went looking for the SIP ALG in a Netgear router he extracted the firmware and found a kernel module containing ftp_decode and sip_decode2. Armis’s tested list included OpenWrt and VyOS plus a category they simply called “various consumer grade Linux routers”, and the H.323 analysis that produced the any-internal-host finding came from reading the netfilter source, then confirmed on commercial firewalls from three vendors3.
The kernel default does not reach you. Linux 4.7 turned automatic helper assignment off in 2016, but only if the kernel is new enough and nobody turned it back on. Armis found VyOS setting nf_conntrack_helper back to 1 explicitly, and noted that plenty of Linux-based products re-enable it “as it is still useful for many users”3. A 2014 kernel in a 2026 product gets the 2014 behaviour, and a current kernel with the switch flipped gets the same. Neither shows up on a datasheet.
Knowing the netfilter model tells you what to ask of every other box. The three questions from the expectation section are not Linux questions. They are the questions. Every vendor has the same object under a different name, the documentation almost never gives you the answers, and knowing what the reference implementation does is how you work out what to test.
Do the Linux work properly, then read every other vendor against it.
The Linux Helpers, Properly
Start with what is actually loaded:
lsmod | grep -E 'nf_conntrack|nf_nat'
The helper modules are the ones named after protocols — nf_conntrack_ftp, _sip, _h323, _irc, _tftp, _pptp, _snmp, _amanda, _sane, _netbios_ns and _talk — each with a matching nf_nat_* where addresses get rewritten. Then check automatic assignment, the switch deciding whether a loaded module does anything by itself:
sysctl net.netfilter.nf_conntrack_helper
Zero is what you want, and the default from Linux 4.7 onward5. One means every loaded helper is live on every flow matching its port, from any address — the 2015 behaviour, and the one the attacks in this post assume.
Then watch conntrack -L expect on a live firewall. With helpers off it stays empty; with them on, put a capture beside it and watch rows appear as people use the network. Worth doing once: nothing makes the point faster than watching an inbound permission you did not write appear and disappear in front of you.
If automatic assignment is off and you still want a helper on a specific flow, the sanctioned way is an explicit rule, which bounds it to one destination and one port:
iptables -t raw -A PREROUTING -p tcp --dport 21 -d 192.0.2.10 -j CT --helper ftp
The nftables equivalent declares a ct helper object and attaches it with ct helper set in the prerouting chain, which is the same discipline with better syntax.
Note what that rule is: a documented, approved, business-justified inbound exception, written by a person, where an auditor can read it. Which the automatic version could never be.
If you want them gone rather than dormant, and on a firewall you should, stop the modules loading at all:
for m in ftp sip h323 irc tftp pptp snmp amanda sane netbios_ns talk; do
echo "install nf_conntrack_$m /bin/false"
done > /etc/modprobe.d/no-conntrack-helpers.conf
install ... /bin/false rather than blacklist is deliberate: blacklist only stops loading by alias, and anything asking for the module by name still gets it. And if a distribution’s firewall layer loads them for you — firewalld does when a zone has the FTP or TFTP service enabled — that is the layer to fix, because it will helpfully put them back.
Every Other Badge, And How To Turn It Off
Check your own version rather than trusting anything on the internet, mine included; these defaults move between releases and models.
pfSense and OPNsense are the proof that the argument is over. There is no SIP ALG to disable, because there has never been one to enable. They are among the most widely deployed firewall distributions there are, they run telephony for a great many organisations, and if a helper were genuinely required for modern VoIP that would not be possible. The FTP proxy went the same way: Netgate took it out of the base system in January 2015.
OpenBSD’s design is the one everyone else should have copied. pf does not rewrite payloads in the forwarding path at all. If you want FTP helped you run ftp-proxy, a separate user-space daemon, and write an explicit divert-to rule sending the control connection to it26. Three properties fall out at once: it is off unless you deliberately turn it on, it only ever sees traffic you named in a rule, and a bug in it crashes a userland process rather than the packet path. That is what opt-in looks like when somebody designs it rather than bolting it on.
OpenWrt does not ship the ALG modules, and automatic assignment stays off if you install them.
Cisco ASA and FTD carry inspection engines in the default global policy, and no inspect sip is Cisco’s own advice during an incident:
policy-map global_policy
class inspection_default
no inspect sip
no inspect h323 h225
no inspect h323 ras
no inspect skinny
On FTD it is configure inspection sip disable from the device CLI16. IPsec pass-through is the one Cisco got right: inspect ipsec-pass-thru is not in the default policy at all, so unless somebody added it deliberately there is nothing to remove20.
Cisco IOS and IOS XE have it on by default too — “NAT support for SIP is enabled by default on port 5060”, in Cisco’s own words7, and the same for H.323:
no ip nat service sip tcp port 5060
no ip nat service sip udp port 5060
no ip nat service h225
Juniper SRX enables SIP and H.323 on branch models and not on the high-end ones, which on its own tells you what Juniper’s engineers think of them. Find out where you stand with show security alg status, then:
set security alg h323 disable
set security alg sip disable
set security alg ftp disable
set security alg ike-esp-nat disable
FortiGate inspects VoIP by default through the VoIP profile, with a kernel session helper underneath it. Fortinet’s documented sequence removes the helper first27:
config system session-helper
show
delete <the sip entry>
end
config system settings
set default-voip-alg-mode kernel-helper-based
end
Read your own show output for the entry number rather than copying one; it moves between models and releases, and Fortinet warn a reboot is often needed.
Check Point is the awkward one to audit, because there is no single switch. The helper is a property of the service object in the rule, so the predefined SIP service gets you the protocol handler and everything it does. Avoiding it means your own plain UDP or TCP service on port 5060 with the protocol type set to none, placed above anything still using the built-ins. “Is the ALG on?” is therefore not a question a settings page can answer. As such, be careful accepting anybody’s word that it is disabled.
Palo Alto gives you a per-application toggle, and their own documentation says the SIP ALG “creates dynamic NAT pinholes”8. Objects, Applications, find sip, tick Disable ALG, commit.
MikroTik ships ten helpers under /ip firewall service-port — SIP, H.323, FTP, IRC, TFTP, PPTP, RTSP and more — documented in a line each, with no security warning on the page. List first, then disable what you find:
/ip firewall service-port print
/ip firewall service-port set [find name=sip] disabled=yes
/ip firewall service-port set [find name=h323] disabled=yes
/ip firewall service-port set [find name=ftp] disabled=yes
Consumer and ISP-supplied routers. Look for “SIP ALG”, “SIP helper”, “VoIP passthrough”, “IPsec passthrough” or “application layer gateway”, usually under advanced NAT. On many ISP-supplied ones there is no setting at all — which tells you whether that box belongs on a network you are responsible for.
Whatever the platform, finish the same way: prove it. Put a capture on the outside interface, send a crafted PORT or REGISTER line at the relevant port from outside, and confirm nothing opens. A setting you have not tested is a belief.
Most Of What Breaks Is Already Dead
Be honest about the cost, because it is the basis for asking anyone to do this. Turning off the SIP helper on a network with badly configured phones can break calls, usually one-way audio or registrations that drop. Turning off the FTP helper breaks active-mode FTP outbound. Turning off the H.323 helper breaks H.323, if you still have any. Expect at least one of those if you do it in a single change on a network nobody has looked at in years.
Now read the list back, and notice that almost every protocol a helper serves is one the rest of the industry already buried. H.323 lost to SIP twenty years ago. PPTP has been indefensible since 1998, a case I have made at length in IPsec Was a Good Idea. IRC direct transfers belong to a decade nobody is nostalgic for. NetBIOS name service, the community-string versions of SNMP, the scanner discovery protocol and the old backup protocol are local-network relics never meant to cross a boundary. Plain FTP is gone from the browsers entirely — Firefox removed it in July 2021 and Chrome that October, both on the grounds that usage was negligible and the security was not worth the maintenance28.
So “we cannot turn the helper off, something will break” is usually an argument for keeping a dead protocol on life support to justify a feature that opens ports for strangers. Turning it off does not break your network. It exposes the one thing on it that should have been retired years ago, which is information you wanted anyway.
SIP is the genuine exception and the only one. Everything else on that list is an argument you should be glad to lose, and none of it is unfixable, because every protocol involved solved its own translation problem years ago, in the protocol, where it belongs.
What To Do Instead
FTP. Passive mode, in the specification since 1985 and the default in every client for twenty years: both connections open outbound and there is nothing left for a helper to do. And if you are moving files between organisations in 2026, FTP is not the protocol for it — SFTP and FTPS are encrypted, and no helper touches either.
SIP and everything else real-time. The endpoint asks a server on the internet what its public address and port look like from outside, offers every candidate path it has, and the two ends test them against each other and keep one that works, with a relay where no direct path exists. That is STUN, TURN and ICE, and it is what every browser on earth does for every video call, through every kind of translator, with no ALG in the path. If your phone system cannot do it in 2026, the problem is your phone system.
IPsec. NAT traversal, which is to say RFC 3947 and RFC 3948: the two ends notice the translator during the key exchange and wrap ESP in UDP 4500 for the rest of the session21, with no gate on any box in between.
H.323. Retire it. SIP won that argument around 2005, so there is no migration to plan, only a deletion. Direct transfers, TFTP, SNMP, NetBIOS, scanner discovery and the backup protocol go the same way: none of them has any business crossing a boundary.
IPv6. None of this exists there, because there is no translation and so nothing for a helper to rewrite. A host has its own address, the address in the payload is true, and a stateful firewall permits what you told it to and nothing else. Every problem in this post descends from address translation, and that descends from not deploying IPv6 — an argument I have made elsewhere and will not repeat.
Now the part I am not going to be diplomatic about.
If somebody tells you to turn these on — a supplier, a telephony installer, a managed service, an integrator on a framework — they are not a network engineer and they are not a security specialist. They may be very good at the thing they actually do, and this will not be it. The correct answer to a phone system that needs a firewall to rewrite its signalling is to fix the phone system, and anyone telling you to open your boundary to a string-matching parser instead either does not know what an expectation is or does not care. We are not amateur hour. There has been a right way to do this in standards track documents since 2007, and “just enable the SIP helper” is the sound of somebody reaching for whatever closes the ticket today.
Ask them, in the room, what the expectation’s source address wildcard is set to. If the question lands as a surprise, you have your answer, and it was never about the protocol.
If You Are Still Running Them, Can You Call Yourself A Professional?
That is a genuine question and it deserves a genuine answer, so here are three, because there are three cases. It turns on whether you know, and knowing is not something that happens to you. Making sure you know is the job.
If there is a SIP or H.323 or FTP helper enabled on a boundary you are responsible for, and you cannot say without looking it up what an expectation is, which of its fields are wildcards, who supplies the values, and what your certification submission claims about inbound rules — then no. Not on this. You have not chosen a configuration, you have inherited a default and never read it. The failing is not the gap; everybody has gaps and I have had this one. It is building a boundary across a gap you never closed, then signing something saying the boundary is sound.
If you know exactly what it does, and it is on because a regulator names the protocol, or a partner’s kit terminates nothing else, or the phone system is replaced in March and this has to hold until then — yes, obviously, and you are doing the job properly. Those are real constraints and I have worked round worse. What makes it professional rather than negligent is that you wrote down which helper, on which interface, for which flow, why, and the date it comes off. Which is the paperwork the firewall control was asking for anyway.
The indefensible case is the middle one. Knowing enough to be uneasy, and leaving it because nobody has made you justify it. That is not engineering. It is habit with a change number attached, and it is how a feature a Best Current Practice told you to turn off in January 2007 is still on in 2026.
This matters most when you are paying somebody for judgement, because you cannot inspect judgement on delivery. So inspect it before you sign. Ask what their standard build does with protocol helpers and why, ask what happens if somebody on the guest network opens a link, and ask which internal devices would be reachable with the H.323 helper left on — then watch whether they say “only the machine that clicked”, because that is the wrong answer a competent-sounding person gives. You will know inside two minutes whether you are being told something or read to, and two minutes is a much cheaper test than an incident.
If the answer comes back as a shrug and you sign anyway, that is a decision as well. It has just stopped being theirs and become yours.
Nobody Was Ever Made To Justify It
I want to be fair to the people who built these things, because they deserve it. In 1994 the helper was a reasonable answer to a real problem. Addresses were running short, NAT was the pragmatic fix, a handful of important protocols did not survive it, and the options were to change every FTP client on earth or teach the box to read. They taught the box to read, shipped the safest defaults they could think of, and wrote warnings into the source about what it could be made to do. Those warnings are still there. I quoted one.
What went wrong afterwards is not a technical failure. It is that nothing in this industry ever required anybody to look at it again. The IETF said turn them off and had no power to make anyone. The kernel changed its default and could not reach the devices already shipped. Researchers proved it four times in sixteen years, and each time the fix landed somewhere other than the firewall. Meanwhile the default stayed on — not because anyone defended it, but because a default nobody argues about survives indefinitely, and no department anywhere has ending things as its job.
That is the pattern, and it is bigger than one firewall feature. This trade is excellent at maintaining and hopeless at stopping. Maintenance is budgeted, staffed, billable and safe; retirement needs one person to put their name on a change with no upside if it goes well and their name all over it if it does not. So the thing stays, and one day somebody finds that it opens ports to your printer.
The tell, for me, is that phrase in Cisco’s own documentation: the ALG creates a NAT door. Not a filter. Not a control. A door, in the wall you paid for, opened by whoever gets a packet to it with the right words at the front — and the settled response has been to ask the people walking past not to try the handle.
You can close yours this afternoon, and that is the part worth ending on. Not the attacks; the attacks are only what happens when nobody does. Go and look at what your boundary permits inbound that you never wrote down, decide whether you meant it, and take out the ones you did not — putting a date next to whatever you kept.
That is all a boundary has ever been: a list of things somebody chose to allow, and could say why. Anything on it nobody chose is not security. It is furniture.
Samy Kamkar — NAT Pinning, 5 January 2010. The original browser-to-ALG attack, using a hidden form to make a browser emit an IRC
DCC CHATor an FTP227response line so that the router’s helper opens an inbound port. “No XSS or CSRF required.” ↩︎ ↩︎ ↩︎ ↩︎Samy Kamkar — NAT Slipstreaming, 31 October 2020, updated January 2021. Summarised by the author as letting “an attacker to remotely access any TCP/UDP service bound to a victim machine, bypassing the victim’s NAT/firewall (arbitrary firewall pinhole control), just by the victim visiting a website”. Carries the segment-boundary technique, the note that the SIP handler “will bail unless the method (eg, REGISTER) occurs at the start of the data portion of the packet”, and the Netgear firmware analysis. ↩︎ ↩︎ ↩︎ ↩︎
Ben Seri and Gregory Vishnepolsky, Armis — NAT Slipstreaming v2.0, 26 January 2021. The H.323 call-forwarding primitive, the relay bypass of the browsers’ restricted-port list, the tested product list (OpenWrt, VyOS, consumer Linux routers, FortiGate, Cisco ASAv and csr1000v, HPE vsr1000, SonicWall TZ300), the disclosure timeline, and the conclusion that “resolving the issue will require a fundamental change of their implementations by various router/firewall vendors”. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
RFC 4787 — Network Address Translation (NAT) Behavioral Requirements for Unicast UDP, January 2007, BCP 127. Section 7 carries REQ-10, and the observation that “Certain NATs have these ALGs turned on permanently, others have them turned on by default but allow them to be turned off”. ↩︎ ↩︎ ↩︎
Pablo Neira Ayuso — netfilter: nf_ct_helper: disable automatic helper assignment, commit
3bb398d9, 25 April 2016, shipped in Linux 4.7. Changes the default ofnf_conntrack_helperfrom enabled to disabled. ↩︎ ↩︎ ↩︎Chromium source —
net/base/port_util.cc. ItskRestrictedPortsarray includes 69, 137, 139, 161, 554, 1719, 1720, 1723, 5060, 5061, 6566 and 10080. Adam Rice’s announcement of the SIP ports, 5 November 2020: “a carefully-crafted HTTP request to port 5060 on an attacker’s server can fool some NAT devices into treating it as a SIP packet and setting up port forwarding.” ↩︎ ↩︎Cisco — SIP ALG Hardening for NAT and Firewall, IP Addressing Configuration Guide, Cisco IOS XE 17.x. “SIP ALG creates a firewall pinhole or a Network Address Translation (NAT) door based on the first value in the Via header field.” NAT support for SIP is “enabled by default on port 5060”. See also Using Application-Level Gateways with NAT, which states SIP and H.323 are on by default. ↩︎ ↩︎
Palo Alto Networks — Disable the SIP Application-level Gateway (ALG). “SIP ALG creates dynamic NAT pinholes but may interfere with VoIP applications that have NAT traversal capabilities, causing communication failures.” ↩︎ ↩︎
RFC 2663 — IP Network Address Translator (NAT) Terminology and Considerations, August 1999. Section 2.9 is where the Application Level Gateway is defined. ↩︎
RFC 3234 — Middleboxes: Taxonomy and Issues, February 2002. The layer-violation line is section 2.11; the cost of extra boxes in the path is section 5. ↩︎ ↩︎
Eric Leblond, Pablo Neira Ayuso, Patrick McHardy, Jan Engelhardt and Mr Dash Four — Secure use of iptables and connection tracking helpers. “This system relies on parsing of data coming either from the user or the server. It is therefore vulnerable to attack.” Source of the IRC wildcard quote; documents the
nf_conntrack_helpersysctl and theCT --helpertarget. ↩︎ ↩︎Linux kernel source —
net/netfilter/nf_conntrack_ftp.c. Theloosemodule parameter defaults to false, guarding the case where thePORTaddress is not the client’s own; the comment names the risk as “DMZ machines opening holes to internal networks, or the packet filter itself”. ↩︎netfilter — H.323 conntrack/NAT helper, by the author of the module. Includes the call-forwarding scenario that lets a session refer to a third-party address. ↩︎
David Leadbeater — NAT-Again: IRC NAT helper flaws, August 2022. Demonstrates the ping-echo trigger, notes it can also scan, unmask cloaked users and disconnect them by naming port 0, and recommends: “Potentially entirely deprecate and remove nf_conntrack_irc, it’s unclear it has much use anymore.” ↩︎ ↩︎
CVE-2022-2663 — “An issue was found in the Linux kernel in nf_conntrack_irc where the message handling can be confused and incorrectly matches the message. A firewall may be able to be bypassed when users are using unencrypted IRC with nf_conntrack_irc configured.” ↩︎ ↩︎
Cisco — advisory for CVE-2018-15454, 31 October 2018. The mitigation given is
no inspect sipon ASA andconfigure inspection sip disableon FTD; NVD records that at publication, “Software updates that address this vulnerability are not yet available.” ↩︎ ↩︎RFC 3715 — IPsec-Network Address Translation (NAT) Compatibility Requirements, March 2004. Section 2.1 item (f) covers SPI selection against NAT; section 2.3 is titled Helper Incompatibilities and carries the quoted lines about IKE cookie de-multiplexing and ISAKMP payload parsing. ↩︎ ↩︎
Juniper — IKE and ESP ALG, Application Layer Gateways User Guide. Source of the gate description, the note that NAT-T traffic on port 4500 is not processed by the ALG, and the warning that where two clients share a translated address the device “will be unable to distinguish and route return traffic properly”. ↩︎ ↩︎
Cisco — IPsec Pass Through Inspection, ASA Firewall CLI Configuration Guide 9.20. “IPsec Pass Through application inspection provides convenient traversal of ESP (IP protocol 50) and AH (IP protocol 51) traffic associated with an IKE UDP port 500 connection.” Not in the default policy; the supplied
_default_ipsec_passthru_map“sets no maximum limit on ESP connections per client”. ↩︎ ↩︎RFC 3947 — Negotiation of NAT-Traversal in the IKE, and RFC 3948 — UDP Encapsulation of IPsec ESP Packets, both January 2005. ↩︎ ↩︎
Cole Dishington — netfilter: nf_conntrack: Add conntrack helper for ESP/IPsec, May 2021, third revision. Reviewed on netfilter-devel and not merged;
net/netfilterin mainline still carries nonf_conntrack_proto_esp.c. ↩︎NCSC and IASME — Cyber Essentials: Requirements for IT Infrastructure v3.3, April 2026. Control 1, Firewalls, applies to “boundary firewalls, desktop computers, laptops, routers, servers, IaaS, PaaS, SaaS”; the three requirements quoted are its own words. ↩︎ ↩︎
RFC 3027 — Protocol Complications with the IP Network Address Translator, January 2001. “The purpose of this document is to identify the protocols and applications that break with NAT enroute.” ↩︎
WHATWG Fetch — pull request 1109, the standards change that added the bad-port entries across browsers. ↩︎
OpenBSD —
ftp-proxy(8). Control connections reach it only because you sent them there: “FTP control connections should be redirected into the proxy using the pf(4) divert-to command, after which the proxy connects to the server on behalf of the client.” ↩︎Fortinet — Technical Tip: Disabling VoIP Inspection. Documents removing the SIP entry from
config system session-helper,set default-voip-alg-mode kernel-helper-based, and notes that re-enabling requires a restart. ↩︎Mozilla — Stopping FTP support in Firefox 90, 20 July 2021, and Google — Deprecations and removals in Chrome 95, October 2021: “Use of FTP in the browser is sufficiently low that it is no longer viable to invest in improving the existing FTP client.” ↩︎