Denis Machard

My technical gists

Infrastructure background, developer mindset. I build things for pleasure.
    @github @mastodon @rss

    DNS tunneling protection with DNSdist

    A DNSdist configuration example to attempt to block DNS tunnelling.

    The global approach is to blacklist IP when some malicious behaviour are detected:

    • Uncommon DNS Qtype like NULL, PRIVATE are blocked
    • A generic regex to block long successive DNS label with whitelist to avoid false-positive
    • Apply some rate limitations per IP

    The latest version of the configuration can be downloaded from github.

    -- Generic rules to block malicious dns traffic like DNS tunneling
    
    -- remote security events logger based on dnstap
    security_dnstap_logger = newFrameStreamTcpLogger("10.0.0.100:6000")
    
    -- trusted domains to exclude
    trusted_domains = newSuffixMatchNode()
    trusted_domains:add(newDNSName("google.fr."))
    
    -- blacklist ip during 60s
    blacklisted_ips = TimedIPSetRule()
    local function blacklistIP(dq)
       blacklisted_ips:add(dq.remoteaddr, 60)
       return DNSAction.Refused
    end
    addAction(blacklisted_ips:slice(), RCodeAction(DNSRCode.REFUSED))
    
    -- match uncommon qtype like NULL (10), PRIVATE (65399) - works fine to block iodine
    addAction(AndRule({OrRule({QTypeRule(10), QTypeRule(65399)}), NotRule(SuffixMatchNodeRule(trusted_domains, true)) }), SetTagAction('malicious_qtypes', 'matched'))
    
    -- match long qname - 2 labels with a minimum of 50 bytes each - works fine to block tools like iodine, dnscat2, dns2tcp...
    -- to avoid false-positive. The regex has been tested on the top 1 million list exposed by Cisco Umbrella http://s3-us-west-1.amazonaws.com/umbrella-static/top-1m.csv.zip
    addAction(AndRule({RegexRule('([^.]{50,}\\.){2,}'), NotRule(SuffixMatchNodeRule(trusted_domains, true)) }), SetTagAction('malicious_longqnames', 'matched'))
    
    -- rate limiting on TXT, CNAME and MX
    addAction(AndRule({ MaxQPSIPRule(5, 32, 64, 5), OrRule({QTypeRule(DNSQType.TXT), QTypeRule(DNSQType.CNAME), QTypeRule(DNSQType.MX)}), NotRule(SuffixMatchNodeRule(trusted_domains, true)) }), SetTagAction('malicious_ratelimiting', 'matched'))
    
    -- logging malicious queries on remote dnstap logger
    addAction(TagRule('malicious_qtypes'), DnstapLogAction("event_deniedqtypes_detected", security_dnstap_logger))
    addAction(TagRule('malicious_longqnames'), DnstapLogAction("event_longqnames_detected", security_dnstap_logger))
    addAction(TagRule('malicious_ratelimiting'), DnstapLogAction("event_ratelimiting_detected", security_dnstap_logger))
    
    -- finally refuses and blacklist client ip during limited time
    addAction(TagRule('malicious_qtypes'), LuaAction(blacklistIP))
    addAction(TagRule('malicious_longqnames'), LuaAction(blacklistIP))
    addAction(TagRule('malicious_ratelimiting'), LuaAction(blacklistIP))
    
    -- Update the dynamic blocks with refused reply by default
    setDynBlocksAction(DNSAction.Refused)
    
    -- Rate exceeded detection with automatic ip blacklisting during 60s
    --  * max bw to 1000bytes/s during 5s
    local dbr = dynBlockRulesGroup()
    dbr:setResponseByteRate(1000, 5, "Exceeded resp BW rate", 60)
    
    -- check dynamic rule every second
    function maintenance()
      dbr:apply()
    end
    
    propulsed by hugo and hugo-theme-gists