1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
local bit = require "bit"
local comm = require "comm"
local nmap = require "nmap"
local shortport = require "shortport"
local string = require "string"
description = [[
Checks a DNS server for the predictable-port recursion vulnerability.
Predictable source ports can make a DNS server vulnerable to cache poisoning
attacks (see CVE-2008-1447).
The script works by querying porttest.dns-oarc.net (see
https://www.dns-oarc.net/oarc/services/porttest). Be aware that any
targets against which this script is run will be sent to and
potentially recorded by one or more DNS servers and the porttest
server. In addition your IP address will be sent along with the
porttest query to the DNS server running on the target.
]]
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
author = [[
Script: Brandon Enright <bmenrigh@ucsd.edu>
porttest.dns-oarc.net: Duane Wessels <wessels@dns-oarc.net>
]]
---
-- @usage
-- nmap -sU -p 53 --script=dns-random-srcport <target>
-- @output
-- PORT STATE SERVICE REASON
-- 53/udp open domain udp-response
-- |_dns-random-srcport: X.X.X.X is GREAT: 26 queries in 1.2 seconds from 26 ports with std dev 17905
-- This script uses (with permission) Duane Wessels' porttest.dns-oarc.net
-- service. Duane/OARC believe the service is valuable to the community
-- and have no plans to ever turn the service off.
-- The likely long-term availability makes this script a good candidate
-- for inclusion in Nmap proper.
categories = {"external", "intrusive"}
portrule = shortport.portnumber(53, "udp")
action = function(host, port)
-- TXID: 0xbeef
-- Flags: 0x0100
-- Questions: 1
-- Answer RRs: 0
-- Authority RRs: 0
-- Additional RRs: 0
-- Query:
-- Name: porttest, dns-oarc, net
-- Type: TXT (0x0010)
-- Class: IN (0x0001)
local query = string.char( 0xbe, 0xef, -- TXID
0x01, 0x00, -- Flags
0x00, 0x01, -- Questions
0x00, 0x00, -- Answer RRs
0x00, 0x00, -- Authority RRs
0x00, 0x00, -- Additional RRs
0x08) .. "porttest" ..
string.char( 0x08) .. "dns-oarc" ..
string.char( 0x03) .. "net" ..
string.char( 0x00, -- Name terminator
0x00, 0x10, -- Type (TXT)
0x00, 0x01) -- Class (IN)
local status, result = comm.exchange(host, port, query, {proto="udp",
timeout=20000})
-- Fail gracefully
if not status then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: TIMEOUT"
else
return
end
end
-- Update the port
nmap.set_port_state(host, port, "open")
-- Now we need to "parse" the results to check to see if they are good
-- We need a minimum of 5 bytes...
if (#result < 5) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Malformed response"
else
return
end
end
-- Check TXID
if (string.byte(result, 1) ~= 0xbe
or string.byte(result, 2) ~= 0xef) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Invalid Transaction ID"
else
return
end
end
-- Check response flag and recursion
if not (bit.band(string.byte(result, 3), 0x80) == 0x80
and bit.band(string.byte(result, 4), 0x80) == 0x80) then
if (nmap.verbosity() >= 1 or nmap.debugging() >= 1) then
return "ERROR: Server refused recursion"
else
return
end
end
-- Check error flag
if (bit.band(string.byte(result, 4), 0x0F) ~= 0x00) then
if (nmap.verbosity() >= 1 or nmap.debugging() >= 1) then
return "ERROR: Server failure"
else
return
end
end
-- Check for two Answer RRs and 1 Authority RR
if (string.byte(result, 5) ~= 0x00
or string.byte(result, 6) ~= 0x01
or string.byte(result, 7) ~= 0x00
or string.byte(result, 8) ~= 0x02) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Response did not include expected answers"
else
return
end
end
-- We need a minimum of 128 bytes...
if (#result < 128) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Truncated response"
else
return
end
end
-- Here is the really fragile part. If the DNS response changes
-- in any way, this won't work and will fail.
-- Jump to second answer and check to see that it is TXT, IN
-- then grab the length and display that text...
-- Check for TXT
if (string.byte(result, 118) ~= 0x00
or string.byte(result, 119) ~= 0x10)
then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Answer record not of type TXT"
else
return
end
end
-- Check for IN
if (string.byte(result, 120) ~= 0x00
or string.byte(result, 121) ~= 0x01) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Answer record not of type IN"
else
return
end
end
-- Get TXT length
local txtlen = string.byte(result, 128)
-- We now need a minimum of 128 + txtlen bytes + 1...
if (#result < 128 + txtlen) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Truncated response"
else
return
end
end
-- GET TXT record
local txtrd = string.sub(result, 129, 128 + txtlen)
return txtrd
end
|