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.

A plain translator reads the header. A helper reads the payload and opens a hole on what it finds.Same point in the path. One of them reads the letter as well as the envelope.A plain translatorA protocol helperThe packet arriving[IP hdr 192.168.1.29:51000] [ payload ]The packet arriving[IP hdr 192.168.1.29:51000] [ PORT 192,168,1,29,4,0 ]What it doesRewrites the source address and port in the headerFixes the checksumsWrites one row so the reply can be turned back roundForwards itWhat it doesAll of that, and thenreads the payload and finds an address and a port in itrewrites those too, and shifts the sequence numberswrites a second row: permit this inbound connectionTables it keepsTranslation table only. One row, one flow, reversible.Nothing in the payload can add a row.Tables it keepsTranslation table, and an expectation table.A row in the second one is an inbound firewall rule.The second table is the whole subject.It says: when a connection arrives from outside matching this pattern, let it through. Nobody wrote it, nobody approved it.
Two devices at the same point in the path. The plain translator rewrites the header and never reads the payload. The helper reads the payload, rewrites the address it finds there, and adds a row to a second table permitting an inbound connection later. That second table is the subject of this post.

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.

An expectation is an inbound rule with blanks in it, and the blanks are the security modelOne object, five fields, and a timer. Which fields are blank is the entire security model.expect proto=tcp src=<who may connect> sport=<any> dst=<where it lands> dport=<which port> timeout=300HelperWho may connectWhere it landsWhich portWorst caseFTPthe server, pinnedthe client, pinnedfrom the payloadany port on one hostIRCany address at allthe client, pinnedfrom the payloadany port, from anyoneH.323any address at allfrom the payloadfrom the payloadany port on any hostBlue: fixed by the firewall from the connection it can see.  Gold: supplied by the payload.  Pink: left open, by design.1. Which fields are wildcards?A wildcard source means the hole is not reserved for the far end of the conversation that made it. Anyone whoreaches your outside interface first can take it. The IRC helper has to do this, because the protocol genuinelycannot know who is going to connect, which is a fair description of a protocol that should not be helped at all.2. Who supplied the values?Not the firewall. The payload did, and the payload was written by whichever end of the conversation the helperwas reading at the time. There is no authentication anywhere in that sentence.3. Who can the hole point at?On most helpers, the host that created it. On H.323, whatever the payload says, because forwarding names a third party.
An expectation is a firewall rule with some fields left blank and a timer on it. Three questions decide whether it is safe, and they are the right three to ask of any helper on any platform.

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.

Active FTP through a helper: four steps, all of them correct, and only two things ever checkedActive FTP, working exactly as specified, and what got checked before the hole openedFTP client192.168.1.29Firewall + helper203.0.113.10FTP server198.51.100.71  control connection outbound to port 21 — permitted, it started inside2  the client listens, then writes its own address into the streamPORT 192,168,1,29,4,03  the helper actsrewrites the address,creates the expectationPORT 203,0,113,10,4,0expect src=198.51.100.7 dst=192.168.1.29 dport=10244  the server connects inbound, the expectation matches, the data flowsLook at what the firewall verified before step four became possible.That the bytes were on a connection to port 21. That they began with the four characters PORT. That is the wholeof it, because that is all there is to check. FTP carries no signature, no session key and nothing else to test.
Active FTP through a helper, correct at every step. Note what the firewall verified before opening the hole: that the bytes were on port 21 and started with PORT. Nothing else, because there is nothing else to check.

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.

Two columns the helper cannot tell apart, because on the wire there is nothing to tell apartThe protocol as designed, and a web page. The helper sees one picture.A real clientA hidden form on a pageWhat starts itA person opens an FTP or IRC client and connectsThe client opens a socket and names it in the streamPORT 192,168,1,29,4,0What starts itA person opens a page. That is their whole contribution.A form posts to the attacker's server on the same portPORT 192,168,1,29,4,0What the helper checksdestination port matches          yeskeyword at the start of the data   yessyntax parses                        yesaddress and port present          yesWhat the helper checksdestination port matches          yeskeyword at the start of the data   yessyntax parses                        yesaddress and port present          yesan inbound port is opened, identically, in both columnsWhat differs is the three things the helper cannot see.Whether a client was involved. Whether the user meant it. Who chose the address and the port. None of thoseleave a trace on the wire, so no amount of care in the parser recovers them. Both columns are perfectly well formed.
The same helper, the same pattern match, the same expectation — and no FTP or IRC client anywhere in the picture. Every check the helper performs is satisfied identically in both columns, because on the wire there is no difference to find.

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.

Moving the segment boundary until the keyword lands where the helper readsThe helper reads the first byte of a segment. So the attacker moves where the segments break.As the browser would send itsegment 1POST / HTTP/1.1 Host: ...segment 2...headers... PORT 192,168,...segment 3...1,29,4,0 padding paddingThe keyword sits in the middle of segment two, so the helper reads past it and does nothing.After the attacker sets the segment size, or acknowledges only part of the streamsegment 1POST / HTTP/1.1 Host: ...segment 2...headers and padding...segment 3PORT 192,168,1,29,4,0The keyword is now the first byte of a segment. The helper reads it and opens the port.The two levers, and neither is an attack on TCP.The attacker's server is one end of the connection, so it announces the maximum segment size the victim's stackwill use, and it decides how much of the stream to acknowledge. Acknowledge part of it and the victim retransmitsfrom the offset the attacker picked. Both are ordinary, standards-compliant TCP, used deliberately and on purpose.
Why the padding matters. The helper only reads a protocol keyword when it is the first byte of a segment, so an attacker who controls only the body has to move the segment boundary until it lands there. Both levers are ordinary TCP, used deliberately.

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.

Click to inbound connection in six steps, none of which is a vulnerabilitySix steps. Four of them are ordinary web and TCP. One is your firewall. One is the attacker.1The victim opens a pageAn advert, a link in a message, anything. This is the whole of the user's involvement.ordinary web2The page learns the inside addressHanded over by the browser's media interface, or narrowed down by timing common gateway addresses.ordinary web3The attacker measures the pathAn oversized request with a marker in it, and a packet capture at the far end to see where the breaks fall.ordinary TCP4The page sends the real payloadPadded so the protocol keyword lands on a segment boundary, exactly as the previous diagram shows.ordinary TCP5Your firewall opens the portThe helper recognises the message, rewrites the address in it, and writes the expectation.your firewall6The attacker connects inboundTo your public address, on the mapped port. The expectation matches and the firewall forwards it inside.your firewallCount the vulnerabilities exploited on the victim's machine. There are none.The browser was patched. The operating system was patched. Nothing was installed and no credential was used.Seconds elapsed, one click, and the only component that could have refused was the one built to refuse.
The full chain, from click to inbound connection. Steps one to four are ordinary web and TCP behaviour. Step five is the firewall feature working exactly as documented, and it is the only component that could have refused.

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.

Pinned to one host, or aimed at anything on the networkMost helpers can only reach the machine that clicked. One of them cannot be bounded at all.FTP, SIP, IRCH.323, with call forwardingThe expectation saysdst = the host whose connection made thisThe expectation saysdst = whatever address the payload namedthe machine that clicked192.168.1.29the printerport 9100the cameradefault passwordthe controllerno authenticationBlast radiusOne machine. Every port on it, for the life of the rule,which is bad enough and is survivable. The device ismanaged, patched and running something that logs.Blast radiusEvery address on the network, one message each.Walk the range, read the banners, pick a target. Noneof those devices ever made a request or ran a browser.Call forwarding is the whole difference, and it is a feature, not a fault.A forwarded call is a call to somebody not on the line, so the signalling names a third party and the helper obliges.
Why one helper is in a different category from the rest. Pin the expectation to the host that created it and the attacker reaches one machine. Let call forwarding name a third party, as the protocol requires, and they walk your whole address range.

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.

A port opened by a message the victim never composedNobody inside clicked anything. The victim's own client emitted the trigger, on request.The victim's client192.168.1.29Firewall + IRC helperreading the streamA strangerany other user, any network1  an ordinary ping, with a direct chat request hidden in the payloadPRIVMSG ExampleUser :^APING ^ADCC CHAT x 3325256705 22^A2  the protocol says answer a ping by echoing the payload, so it does3  both of the helper's checks pass, and neither should haveit looks for the keyword anywhere in the stream, not at the start of a framed messageit validates the address against the chat server, not against the host behind the translator4  the expectation exists, and port 22 is open inbound to the victimEverything in that sequence followed its specification.The stranger sent a legal message. The client answered it the way the protocol requires. The helper matched thepattern it was written to match. No user decided anything, and nothing in any log will look the least bit unusual.
The trigger does not have to come from someone you trust, or from a browser, or from anything a user did on purpose. Every piece of software here followed its specification, and the result is an open port and nothing odd in any log.

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.

CVEComponentWhat a crafted packet does
CVE-2018-0051Junos SIP ALGCrashes the flow daemon on SRX and MX; also records that SIP ALG is on by default except on high-end models
CVE-2018-15454Cisco ASA / FTD SIP inspectionReloads the device or pins the CPU
CVE-2022-2663Linux nf_conntrack_ircOpens ports through the firewall, as above
CVE-2023-22412Junos SIP ALG“Specific SIP messages” crash the flow daemon, repeatably
CVE-2023-22415Junos H.323 ALGOut-of-bounds write from “specific H.323 packets”
CVE-2024-21616Junos SIP ALGOne SIP packet exhausts the NAT pool, so genuine traffic stops translating
CVE-2024-26851Linux nf_conntrack_h323Out-of-range bit shift decoding the H.323 bitmap
CVE-2024-39551Junos H.323 ALGMemory 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.

The helper needs cleartext, so keeping the helper means keeping the cleartextYou can have the encryption or you can have the helper. There is no third column.Control channel encryptedHelper workingWhat the helper sees17 03 03 01 a4 9c 2f e1 8b 44 d0 ... ciphertextNo keyword. No address. No port. Nothing to match.What the helper seesREGISTER sip:example ... Contact: 192.168.1.29:5060And so does every other device between you and them.What that costs youThe helper does nothing at all, so the endpoints have tosolve their own translation problem — which every oneof these protocols has been able to do for a decade.What that costs youRegistration credentials, who called whom, and everyinternal address, in clear text, across every networkbetween the two ends. None of which you control.The encrypted mode has existed this longFTP over TLS since 2005  ·  SIP over TLS with encrypted media since the base standard  ·  chat over TLS for decades  ·  H.323 has its own security annexeSo the honest form of "we need the SIP helper" is a sentence nobody says out loud.It is: we need our call signalling to cross untrusted networks in plain text, so a box we do not own can rewrite it.
The trade nobody writes down. A helper works only on payload it can read, so the two columns are mutually exclusive. The right-hand one is what a working SIP ALG asks you to agree to.

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
Three firewall requirements, and what a helper does about each of themCyber Essentials, control one, firewalls. Three obligations, and a helper's answer to each.What the requirements document saysWhat a protocol helper doesResult"block unauthenticated inboundconnections by default"The first obligation, and the reason the control exists.Permits one, on the strength of a stringin a packet. The party that supplied thestring authenticated to nothing at all.fails"ensure inbound firewall rules are approvedand documented by an authorised person,and include the business need"Written at line rate by a kernel module.No person approved it, no person saw it,no document, no business need recorded.fails"remove or disable unnecessary firewallrules, when they are no longer needed"Which presumes somebody decided they were needed.Removed by a timer. A timer is not areview, and nobody ever assessed whetherthe rule was necessary to begin with.failsThis is the first of the five technical controls, not an edge case in the fifth.It applies, in the scheme's own words, to boundary firewalls, desktop computers, laptops, routers and servers.
The three firewall requirements in the Cyber Essentials technical controls, and what a protocol helper does about each one. Three requirements, three failures, on the first of the five controls.

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.

Twenty-five years of the same finding, and the one row that never appearsThe conclusion was reached in 2007. The defaults did not move.WhenWhat happenedWho could actJan 2001RFC 3027 catalogues every protocol that NAT breaks, and what an ALG must do about eachstandards bodyFeb 2002RFC 3234 calls the mechanism "a deliberate layer violation" and warns of extra points of attackstandards bodyJan 2007RFC 4787, a Best Current Practice: NAT ALGs for UDP-based protocols SHOULD be turned offstandards bodyJan 2010NAT Pinning: a hidden form on a web page opens an inbound port on the visitor's machinea researcher2012netfilter gains an explicit attach mechanism and a switch to stop automatic assignmentthe kernelApr 2016Linux changes the default: helpers do nothing without an explicit rule. Ships in 4.7the kernelOct 2020NAT Slipstreaming, then the January 2021 variant that reaches every device on the networkresearchersNov 2020Four browser vendors ship mitigations; the web platform standard gains a forbidden port listthe browsersAug 2022The IRC helper turns out to fire on a message the victim never composeda researcher2023–24Four more ALG vulnerabilities in one vendor's flagship firewall lineresearchersNow read the "who could act" column, and notice who is never in it.In twenty-five years, not one row of this is a firewall vendor pushing an update that turns the feature off on kitalready in the field. The standards body asked, the kernel changed upstream, and neither could reach the boxes.
Twenty-five years of the same conclusion, reached by people who could not fix the thing that needed fixing. The one row that never appears is a firewall vendor turning the feature off on kit already in the field.

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

The middlebox answer and the endpoint answer, side by sideThe same problem, answered twice. One answer put the decision in the middle.A box in the path decidesThe two ends decideHow it worksA device reads the payload as it passesIt rewrites the address it finds written thereIt opens an inbound hole for the connection describedNobody at either end knows any of this happenedHow it worksEach end asks a server what it looks like from outsideEach offers every path it has: local, translated, relayedThe two ends test the paths against each otherThey keep the one that works open with their own trafficWhat it needs from youThe payload readable, so no encryption on the controlchannel. That specific device in that specific path. Onetranslator only, so a carrier-grade one downstreambreaks it. And trust in whoever wrote the text, whichis the part nobody thought about until 2010.What it needs from youOutbound access, and nothing else at all. It worksthrough translators you do not own and cannot see,through two of them stacked, through a mobilenetwork, and with the control channel encryptedend to end, because nothing in the middle reads it.For file transfer it is simpler still: passive mode, where the client opens both connections outbound and there is nothing left for a helper to do.The right-hand column is not a proposal. It is what your browser already does.Every video call made in a browser is negotiated that way, through every kind of translator, with no ALG in the path.
The same problem, solved twice. One answer put the decision in a box in the middle. The other let the two ends work it out between themselves — which is what every browser already does for every video call.

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.


  1. 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 CHAT or an FTP 227 response line so that the router’s helper opens an inbound port. “No XSS or CSRF required.” ↩︎ ↩︎ ↩︎ ↩︎

  2. 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. ↩︎ ↩︎ ↩︎ ↩︎

  3. 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”. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  4. 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”. ↩︎ ↩︎ ↩︎

  5. Pablo Neira Ayuso — netfilter: nf_ct_helper: disable automatic helper assignment, commit 3bb398d9, 25 April 2016, shipped in Linux 4.7. Changes the default of nf_conntrack_helper from enabled to disabled. ↩︎ ↩︎ ↩︎

  6. Chromium source — net/base/port_util.cc. Its kRestrictedPorts array 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.” ↩︎ ↩︎

  7. 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. ↩︎ ↩︎

  8. 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.” ↩︎ ↩︎

  9. RFC 2663 — IP Network Address Translator (NAT) Terminology and Considerations, August 1999. Section 2.9 is where the Application Level Gateway is defined. ↩︎

  10. 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. ↩︎ ↩︎

  11. 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_helper sysctl and the CT --helper target. ↩︎ ↩︎

  12. Linux kernel source — net/netfilter/nf_conntrack_ftp.c. The loose module parameter defaults to false, guarding the case where the PORT address is not the client’s own; the comment names the risk as “DMZ machines opening holes to internal networks, or the packet filter itself”. ↩︎

  13. 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. ↩︎

  14. 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.” ↩︎ ↩︎

  15. 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.” ↩︎ ↩︎

  16. Cisco — advisory for CVE-2018-15454, 31 October 2018. The mitigation given is no inspect sip on ASA and configure inspection sip disable on FTD; NVD records that at publication, “Software updates that address this vulnerability are not yet available.” ↩︎ ↩︎

  17. RFC 4217 — Securing FTP with TLS, October 2005. ↩︎

  18. 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. ↩︎ ↩︎

  19. 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”. ↩︎ ↩︎

  20. 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”. ↩︎ ↩︎

  21. RFC 3947 — Negotiation of NAT-Traversal in the IKE, and RFC 3948 — UDP Encapsulation of IPsec ESP Packets, both January 2005. ↩︎ ↩︎

  22. Cole Dishington — netfilter: nf_conntrack: Add conntrack helper for ESP/IPsec, May 2021, third revision. Reviewed on netfilter-devel and not merged; net/netfilter in mainline still carries no nf_conntrack_proto_esp.c. ↩︎

  23. 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. ↩︎ ↩︎

  24. 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.” ↩︎

  25. WHATWG Fetch — pull request 1109, the standards change that added the bad-port entries across browsers. ↩︎

  26. 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.” ↩︎

  27. 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. ↩︎

  28. 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.” ↩︎