mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
Clean up some typos and differences. Most have been normalized to whatever form of the name occurred in the largest number of scripts. Paulino was contacted directly and requested his email be added to all of his credits.
190 lines
6.3 KiB
Lua
190 lines
6.3 KiB
Lua
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>
|
|
]]
|
|
|
|
---
|
|
-- @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
|