firewall vulnerability?

I have a rule that drops traffic if src is not on the address-list

drop rule works, however it does NOT drop port forwarded traffic to another IP address.

for example firewall drop rule will block dstnat redirect rule but dstnat dst-nat IP:port will not be blocked

Isn’t that strange?

(ROS4.11)

Post the firewall filter rules, the NAT rules, and the address lists together with an exact description of what traffic (source IP/source port and destination IP/destination port tuple) is getting dropped, and what isnt.

accept rules above drop rule:

add action=accept chain=input comment="accept ESTABLISHED" connection-state=\
    established disabled=no
add action=accept chain=input comment="accept RELATED" connection-state=related \
    disabled=no

the Drop rule:

add action=drop chain=input comment="DROP what's not on thelist" \
    disabled=no src-address-list=!thelist

(1.1.1.1 is IP of wan interface)
This port forward traffic will be blocked by the drop rule above:

add action=redirect chain=dstnat comment="Forward to DNS" disabled=no \
    dst-address=1.1.1.1 0 dst-port=53 protocol=udp to-ports=53

This port forward traffic will not be blocked by the drop rule:

add action=dst-nat chain=dstnat comment="" disabled=no dst-address=1.1.1.1 \
    dst-port=3389 protocol=tcp to-addresses=192.168.1.1 to-ports=3389

Well, that’s perfectly expected.

When you’re using destination NAT to translate an router IP to an IP behind the router on the LAN, the packet now carries an IP address that isn’t on the router. So that packet is never in the input chain, it’s in the forward chain, therefore rules in the input chain can’t affect it. ‘redirect’ is a special destination NAT version that rewrites the destination address to the preferred IP address on the interface the router receives the packet through, so those packets will always be in the input chain.

Remember: ‘input’ chain = packets that have a destination IP address that is on a router interface = traffic TO the router, AFTER destination NAT. ‘forward’ chain = packet that is going THROUGH the router, AFTER destination NAT. ‘output’ chain = packet generated by the router itself. ‘prerouting’ mangle and filter = BEFORE destination NAT, both packets TO and THROUGH the router. ‘postrouting’ mangle and filter = just before source NAT.

add action=redirect chain=dstnat comment="Forward to DNS" disabled=no \
    dst-address=1.1.1.1 0 dst-port=53 protocol=udp to-ports=53

If 1.1.1.1 is your interface IP address that rule makes no sense. You’re rewriting a packet with a destination IP address of 1.1.1.1 to have a destination address of 1.1.1.1. That’s a no-op.

Thanks fewi. I’ll try adding this to the filter:

add action=accept chain=forward comment="accept ESTABLISHED" connection-state=\
    established disabled=no
add action=accept chain=forward comment="accept RELATED" connection-state=related \
    disabled=no
add action=drop chain=input comment="DROP what's not on thelist" \
    disabled=no src-address-list=!thelist

The third one of those rules should also be in the forward chain, and without seeing your address list you’d probably want to add an accept based on the in-interface so that LAN hosts can initiate traffic and place it above the drop. If your address list covers all LAN hosts that should work, though.

ver2 :slight_smile:

add action=accept chain=forward comment="accept ESTABLISHED" connection-state=\
    established disabled=no
add action=accept chain=forward comment="accept RELATED" connection-state=related \
    disabled=no
add action=drop chain=forward comment="DROP what's not on thelist" \
    disabled=no in-interface=WAN src-address-list=!thelist

in-interface=WAN should eliminate the need for LAN IP to be on thelist list I think.

That would work, but personally I’d take it a little further. I’m philosophically a huge fan of ‘default deny’. The last statement in any chain should always be “chain=x action=drop” so that you have to explicitly permit traffic further up. Whatever packet somehow (God knows how) circumvents that last rule because some qualifier doesn’t match would be permitted. The final drop rule should have no qualifiers at all outside of the chain name, so that it will literally drop every single packet not accepted higher up.

I’d suggest this:

/ip firewall filter
add chain=forward connection-state=established action=accept
add chain=forward connection-state=related action=accept
add chain=forward connection-state=invalid action=drop
add chain=forward src-address-list=thelist action=accept
add chain=forward in-interface=LAN action=accept
add chain=forward action=drop

add chain=input connection-state=established action=accept
add chain=input connection-state=related action=accept
add chain=input connection-state=invalid action=drop
add chain=input src-address-list=thelist action=accept
add chain=input action=drop

fewi, I agree too, default deny is always a good idea.

Also, just a note about the

connection-state=invalid action=drop

, it makes sense (as it is) higher in the chain so every firewall rule isn’t check against an invalid packet. Order of firewall rules can have a significant performance impact.

It seems to me that if you put “connection-state=invalid” higher in the list; then every packet will be tested for this, even if they are from a valid known connection.

This rule does not check if the packet is valid, it does check if the connection is valid (it will drop a packet who’s not from an established connection, and at the same time don’t have the SYN TCP flag).

So if you put this rule higher, it will test all established connection packets for nothing.

Right, I agree. The reason I picked the invalid rule was to illustrate why there was a drop rule in the mix, whereas the general idea was to put a default drop rule last, also to point out that generally, putting rules that matched more often are better higher in the chain.