skip to content

Your branch ACL denies tcp/22 outbound and permits tcp/443 — how does a user still get an SSH session out?

level: middleimportance: should knowfreq 60%

answer

  1. the denied number never appears
  2. the far end binds what it likes
  3. one permitted flow can carry many
  4. inner ports are payload, not header
  5. ordering is not the problem here

basics

~20 s

By making the traffic present a permitted number: point the client at a server listening on 443, or ride an existing 443 flow as a tunnel. The deny rule never matches because no packet ever carries port 22.

solid answer

~50 s

Two moves, and neither requires defeating the firewall. First, relocation: the far-end host listens for SSH on 443, the client connects there, and the outer five-tuple shows destination port 443 — your tcp/22 deny never matches because port 22 appears nowhere on the wire. Second, encapsulation: one permitted 443 flow carries a tunnel, and arbitrary inner sessions ride inside it, so the inner destination ports never appear in any header the filter reads. The consequence for policy design is the important part: a deny list of ports is only as strong as the least-inspected permit in the same rule base, and a single broad egress permit collapses the rest of the list. Note the adversary is often nobody's idea of an attacker: it is a developer who needs a shell and has found the number that works.

go deeper

for a junior

Know that a server can listen on any port and a client can be pointed at any port, so a deny rule for a service's usual number does not deny the service.

for a middle

Explain both shapes — relocation and encapsulation — and be precise that the inner sessions of a tunnel are payload, so no header-matching rule can reach them.

for a senior

Show the design consequence: one broad egress permit caps the value of every deny beside it, and name the levers that survive without inspection, with their operational cost.

for a principal

Be ready to argue whether a destination allow-list for user endpoints is worth the exception burden it creates, and who absorbs the blocked-at-6pm cost when it is.

## The rule base people imagine they have A branch outbound ACL that reads roughly *deny tcp/22, deny tcp/3389, deny tcp/445, permit tcp/443, permit tcp/80, permit udp/53, deny everything else* looks like a list of blocked capabilities. It is not. It is a list of blocked **numbers**, and the deny lines only ever fire against traffic that presents them. ## Move one: relocate the service A server binds whatever port its operator configures. If the far end — a personal VPS, a cloud instance, a home machine — listens for SSH on 443, the client connects to 443 and the packets on the branch link carry destination port 443. The tcp/22 deny is not bypassed; it is simply never reached, because no packet matches it. This is the plainest demonstration that a port number is a claim: both endpoints agreed to use a different number, and no third party is in a position to contradict them. Relocation needs no tooling and no exploitation. It usually is not even done to evade anything: somebody wanted a shell from a locked-down site, tried the obvious thing, found it blocked, and moved the listener. ## Move two: tunnel inside a permitted flow The stronger form is encapsulation. One flow to destination port 443 is established, and inside it a general-purpose tunnel carries other sessions. From the filter's chair the wire shows a single long-lived TCP connection to port 443 with bytes moving in both directions. The inner sessions — whatever ports they use — are payload. They are not in the outer header, so they are not in the five-tuple, so no rule that reads the five-tuple can act on them. This is why one broad egress permit is structurally different from the others. Every deny in the rule base is conditional on the traffic choosing to present the denied number, and a tunnel guarantees it never will. ## What the filter would need in order to tell To distinguish an interactive tunnel from ordinary traffic on the same port, something on the path would have to look past the header at the session itself, and for encrypted flows that means terminating and re-originating the connection. At a small branch with no inspection appliance that device does not exist. Even where it does, some flows resist it permanently: a client that pins its expected certificate will not accept a substituted one, so for that traffic there is no price at which you get to look inside. ## What you can actually do at a site like this The honest options do not involve seeing the payload: - **Shrink the destination set.** For servers and appliances, permit 443 only to a named set of destinations. A general-purpose tunnel needs a far end you control; if that far end is not on the list, the move fails. This is strong for infrastructure and weak for user laptops, where the legitimate destination set is effectively the internet. - **Default-deny everything else outbound**, so the permit list is explicit and every addition is a decision somebody made and can be reviewed. - **Keep the deny logs.** They will not show the tunnel — it was permitted — but they show the first attempt on 22 before somebody moved the listener, and that attempt is often the only signal you get. - **Watch what a permitted flow looks like in flow records**: long-lived, low-rate, bidirectional sessions to a single destination are a lead, not proof, and pursuing them is detection work rather than filtering work. ## The price, stated plainly Every lever above costs something a person has to absorb. A destination allow-list means somebody maintains it and somebody is blocked at 6pm when a legitimate destination is missing. Default-deny egress means an exception queue and an outage the first time a business tool needs an unlisted port. And the residual, after all of it, is that a permitted port carried something you could not name. The interviewer is checking whether you say that residual out loud or quietly imply the deny list covered it. ## The wrong answer to avoid The common weak response is to reach for rule ordering — "put the deny above the permit" — as if evaluation order were the problem. It is not. Order decides which rule matches when several could; here no rule could, because the packet does not carry the denied value. Order matters enormously for correctness of a rule base, and not at all for this failure.

  • If several services are tunnelled inside one permitted 443 flow, what do your other deny rules see?
    Nothing. The inner sessions exist only as payload inside the outer connection, so the wire carries one five-tuple with destination port 443. Rules that read headers cannot act on values that are not in a header, which is why a single broad permit weakens every deny beside it.
  • Would reordering the ACL so the deny lines come first fix this?
    No. Ordering decides which rule wins when more than one could match. Here the tcp/22 deny cannot match at all, because no packet on the link carries port 22. Reaching for ordering is a sign the candidate has not internalised that the filter only sees what the sender presents.
  • Which of your available levers actually stops the relocated-server case?
    Restricting the permitted destinations. A tunnel or relocated listener needs a far end the user controls; if 443 is permitted only to a named destination set, that far end is not reachable. It works well for servers and appliances and poorly for user laptops, whose legitimate destination set is the whole internet.

saying these in an interview costs you the question

  • Blames rule ordering for the bypass
  • Assumes SSH cannot run on a non-default port
  • Thinks connection tracking would catch the tunnel
  • Believes the inner ports appear somewhere in the header
  • Calls it an exploit rather than a configuration choice

context