mirror of
https://github.com/nmap/nmap.git
synced 2025-12-09 14:11:29 +00:00
o Added dns-safe-recursion-port and dns-safe-recursion-txid (non
default NSE scripts) which use the 3rd party dns-oarc.net to test the source port and transaction ID randomness of a discovered DNS server (assuming it allows recursion at all). These scripts were contributed by Brandon Enright.
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
# Nmap Changelog ($Id$); -*-text-*-
|
||||
|
||||
o Added dns-safe-recursion-port and dns-safe-recursion-txid (non
|
||||
default NSE scripts) which use the 3rd party dns-oarc.net to test
|
||||
the source port and transaction ID randomness of a discovered DNS
|
||||
server (assuming it allows recursion at all). These scripts were
|
||||
contributed by Brandon Enright.
|
||||
|
||||
o Added some Windows and MinGW compatibility patches submitted by
|
||||
Gisle Vanem.
|
||||
|
||||
|
||||
172
scripts/dns-safe-recursion-port.nse
Normal file
172
scripts/dns-safe-recursion-port.nse
Normal file
@@ -0,0 +1,172 @@
|
||||
id = "DNS source port randomness"
|
||||
|
||||
description = "Queries porttest.dns-oarc.net to check for the predictable-port DNS recursion vulnerability. Predictable source ports can make a DNS server vulnerable to cache poisoning attacks (CVE-2008-1447)"
|
||||
|
||||
license = "Script: Same as Nmap--See http://nmap.org/book/man-legal.html\n" ..
|
||||
"porttest.dns-oarc.net: https://www.dns-oarc.net/oarc/services/porttest"
|
||||
|
||||
author = "Script: Brandon Enright <bmenrigh@ucsd.edu>\n" ..
|
||||
"porttest.dns-oarc.net: Duane Wessels <wessels@dns-oarc.net>"
|
||||
|
||||
-- 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 = {"intrusive"}
|
||||
|
||||
require "bit"
|
||||
require "comm"
|
||||
require "shortport"
|
||||
|
||||
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)
|
||||
|
||||
-- This doesn't work without the bytes= setting...
|
||||
local status, result = comm.exchange(host, port, query, {proto="udp",
|
||||
bytes=1,
|
||||
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 (string.len(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 (string.len(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 (string.len(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
|
||||
172
scripts/dns-safe-recursion-txid.nse
Normal file
172
scripts/dns-safe-recursion-txid.nse
Normal file
@@ -0,0 +1,172 @@
|
||||
id = "DNS TXID randomness"
|
||||
|
||||
description = "Queries txidtest.dns-oarc.net to check for the predictable-TXID DNS recursion vulnerability. Predictable TXID values can make a DNS server vulnerable to cache poisoning attacks (CVE-2008-1447)"
|
||||
|
||||
license = "Script: Same as Nmap--See http://nmap.org/book/man-legal.html\n" ..
|
||||
"txidtest.dns-oarc.net: https://www.dns-oarc.net/oarc/services/txidtest"
|
||||
|
||||
author = "Script: Brandon Enright <bmenrigh@ucsd.edu>\n" ..
|
||||
"txidtest.dns-oarc.net: Duane Wessels <wessels@dns-oarc.net>"
|
||||
|
||||
-- This script uses (with permission) Duane Wessels' txidtest.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 = {"intrusive"}
|
||||
|
||||
require "bit"
|
||||
require "comm"
|
||||
require "shortport"
|
||||
|
||||
portrule = shortport.portnumber(53, "udp")
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
-- TXID: 0xbabe
|
||||
-- Flags: 0x0100
|
||||
-- Questions: 1
|
||||
-- Answer RRs: 0
|
||||
-- Authority RRs: 0
|
||||
-- Additional RRs: 0
|
||||
|
||||
-- Query:
|
||||
-- Name: txidtest, dns-oarc, net
|
||||
-- Type: TXT (0x0010)
|
||||
-- Class: IN (0x0001)
|
||||
|
||||
local query = string.char( 0xba, 0xbe, -- TXID
|
||||
0x01, 0x00, -- Flags
|
||||
0x00, 0x01, -- Questions
|
||||
0x00, 0x00, -- Answer RRs
|
||||
0x00, 0x00, -- Authority RRs
|
||||
0x00, 0x00, -- Additional RRs
|
||||
0x08) .. "txidtest" ..
|
||||
string.char( 0x08) .. "dns-oarc" ..
|
||||
string.char( 0x03) .. "net" ..
|
||||
string.char( 0x00, -- Name terminator
|
||||
0x00, 0x10, -- Type (TXT)
|
||||
0x00, 0x01) -- Class (IN)
|
||||
|
||||
-- This doesn't work without the bytes= setting...
|
||||
local status, result = comm.exchange(host, port, query, {proto="udp",
|
||||
bytes=1,
|
||||
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 (string.len(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) ~= 0xba
|
||||
or string.byte(result, 2) ~= 0xbe) 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 (string.len(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 (string.len(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
|
||||
@@ -3,6 +3,9 @@ Entry{ category = "intrusive", filename = "dns-test-open-recursion.nse" }
|
||||
Entry{ category = "default", filename = "RealVNC_auth_bypass.nse" }
|
||||
Entry{ category = "malware", filename = "RealVNC_auth_bypass.nse" }
|
||||
Entry{ category = "vuln", filename = "RealVNC_auth_bypass.nse" }
|
||||
Entry{ category = "intrusive", filename = "dns-safe-recursion-port.nse" }
|
||||
Entry{ category = "intrusive", filename = "SNMPcommunitybrute.nse" }
|
||||
Entry{ category = "auth", filename = "SNMPcommunitybrute.nse" }
|
||||
Entry{ category = "default", filename = "showOwner.nse" }
|
||||
Entry{ category = "safe", filename = "showOwner.nse" }
|
||||
Entry{ category = "default", filename = "SSLv2-support.nse" }
|
||||
@@ -18,6 +21,7 @@ Entry{ category = "safe", filename = "rpcinfo.nse" }
|
||||
Entry{ category = "discovery", filename = "rpcinfo.nse" }
|
||||
Entry{ category = "auth", filename = "bruteTelnet.nse" }
|
||||
Entry{ category = "intrusive", filename = "bruteTelnet.nse" }
|
||||
Entry{ category = "intrusive", filename = "dns-safe-recursion-txid.nse" }
|
||||
Entry{ category = "default", filename = "SMTPcommands.nse" }
|
||||
Entry{ category = "discovery", filename = "SMTPcommands.nse" }
|
||||
Entry{ category = "safe", filename = "SMTPcommands.nse" }
|
||||
@@ -31,6 +35,7 @@ Entry{ category = "demo", filename = "chargenTest.nse" }
|
||||
Entry{ category = "malware", filename = "strangeSMTPport.nse" }
|
||||
Entry{ category = "version", filename = "iax2Detect.nse" }
|
||||
Entry{ category = "demo", filename = "showSMTPVersion.nse" }
|
||||
Entry{ category = "discovery", filename = "ASN.nse" }
|
||||
Entry{ category = "default", filename = "showHTMLTitle.nse" }
|
||||
Entry{ category = "demo", filename = "showHTMLTitle.nse" }
|
||||
Entry{ category = "safe", filename = "showHTMLTitle.nse" }
|
||||
@@ -56,6 +61,9 @@ Entry{ category = "discovery", filename = "finger.nse" }
|
||||
Entry{ category = "demo", filename = "showHTTPVersion.nse" }
|
||||
Entry{ category = "default", filename = "SSHv1-support.nse" }
|
||||
Entry{ category = "safe", filename = "SSHv1-support.nse" }
|
||||
Entry{ category = "default", filename = "popcapa.nse" }
|
||||
Entry{ category = "intrusive", filename = "brutePOP3.nse" }
|
||||
Entry{ category = "auth", filename = "brutePOP3.nse" }
|
||||
Entry{ category = "default", filename = "MySQLinfo.nse" }
|
||||
Entry{ category = "discovery", filename = "MySQLinfo.nse" }
|
||||
Entry{ category = "safe", filename = "MySQLinfo.nse" }
|
||||
|
||||
Reference in New Issue
Block a user