1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 12:41:29 +00:00

o [NSE] Oops, there was a vulnerability in one of our 437 NSE scripts.

If you ran the (fortunately non-default) http-domino-enum-passwords
  script with the (fortunately also non-default)
  domino-enum-passwords.idpath parameter against a malicious server,
  it could cause an arbitrarily named file to to be written to the
  client system.  Thanks to Trustwave researcher Piotr Duszynski for
  discovering and reporting the problem.  We've fixed that script, and
  also updated several other scripts to use a new
  stdnse.filename_escape function for extra safety.  This breaks our
  record of never having a vulnerability in the 16 years that Nmap has
  existed, but that's still a fairly good run. [David, Fyodor]
This commit is contained in:
fyodor
2013-07-29 06:19:24 +00:00
parent 93accf0619
commit f79a11aeeb
10 changed files with 56 additions and 25 deletions

View File

@@ -19,6 +19,18 @@ o [Ncat] Added --lua-exec. This feature is basically an equivalent of ncat
redirecting all stdin and stdout operations to the socket connection. redirecting all stdin and stdout operations to the socket connection.
[Jacek Wielemborek] [Jacek Wielemborek]
o [NSE] Oops, there was a vulnerability in one of our 437 NSE scripts.
If you ran the (fortunately non-default) http-domino-enum-passwords
script with the (fortunately also non-default)
domino-enum-passwords.idpath parameter against a malicious server,
it could cause an arbitrarily named file to to be written to the
client system. Thanks to Trustwave researcher Piotr Duszynski for
discovering and reporting the problem. We've fixed that script, and
also updated several other scripts to use a new
stdnse.filename_escape function for extra safety. This breaks our
record of never having a vulnerability in the 16 years that Nmap has
existed, but that's still a fairly good run. [David, Fyodor]
o [NSE] Added teamspeak2-version.nse by Marin Maržić. o [NSE] Added teamspeak2-version.nse by Marin Maržić.
o Nmap's routing table is now sorted first by netmask, then by metric. o Nmap's routing table is now sorted first by netmask, then by metric.

View File

@@ -1195,4 +1195,36 @@ function pretty_printer (obj, printer)
return aux(obj, "") return aux(obj, "")
end end
-- This pattern must match the percent sign '%' since it is used in
-- escaping.
local FILESYSTEM_UNSAFE = "[^a-zA-Z0-9._-]"
---
-- Escape a string to remove bytes and strings that may have meaning to
-- a filesystem, such as slashes. All bytes are escaped, except for:
-- * alphabetic <code>a</code>-<code>z</code> and <code>A</code>-<code>Z</code>, digits 0-9, <code>.</code> <code>_</code> <code>-</code>
-- In addition, the strings <code>"."</code> and <code>".."</code> have
-- their characters escaped.
--
-- Bytes are escaped by a percent sign followed by the two-digit
-- hexadecimal representation of the byte value.
-- * <code>filename_escape("filename.ext") --> "filename.ext"</code>
-- * <code>filename_escape("input/output") --> "input%2foutput"</code>
-- * <code>filename_escape(".") --> "%2e"</code>
-- * <code>filename_escape("..") --> "%2e%2e"</code>
-- This escaping is somewhat like that of JavaScript
-- <code>encodeURIComponent</code>, except that fewer bytes are
-- whitelisted, and it works on bytes, not Unicode characters or UTF-16
-- code points.
function filename_escape(s)
if s == "." then
return "%2e"
elseif s == ".." then
return "%2e%2e"
else
return (string.gsub(s, FILESYSTEM_UNSAFE, function (c)
return string.format("%%%02x", string.byte(c))
end))
end
end
return _ENV; return _ENV;

View File

@@ -103,7 +103,7 @@ action = function(host, port)
helper:disconnect() helper:disconnect()
if ( status and data and path ) then if ( status and data and path ) then
local filename = ("%s/%s.id"):format(path, username ) local filename = path .. "/" .. stdnse.filename_escape(u_details.fullname .. ".id")
local status, err = saveIDFile( filename, data ) local status, err = saveIDFile( filename, data )
if ( status ) then if ( status ) then

View File

@@ -68,7 +68,7 @@ categories = {"external", "discovery", "intrusive"}
local HOSTMAP_SERVER = "www.bfk.de" local HOSTMAP_SERVER = "www.bfk.de"
local filename_escape, write_file local write_file
hostrule = function(host) hostrule = function(host)
return not ipOps.isPrivate(host.ip) return not ipOps.isPrivate(host.ip)
@@ -106,7 +106,7 @@ action = function(host)
local filename_prefix = stdnse.get_script_args("hostmap-bfk.prefix") local filename_prefix = stdnse.get_script_args("hostmap-bfk.prefix")
if filename_prefix then if filename_prefix then
local filename = filename_prefix .. filename_escape(host.targetname or host.ip) local filename = filename_prefix .. stdnse.filename_escape(host.targetname or host.ip)
local status, err = write_file(filename, hostnames_str .. "\n") local status, err = write_file(filename, hostnames_str .. "\n")
if status then if status then
output_tab.filename = filename output_tab.filename = filename
@@ -118,13 +118,6 @@ action = function(host)
return output_tab return output_tab
end end
-- Escape some potentially unsafe characters in a string meant to be a filename.
function filename_escape(s)
return string.gsub(s, "[\0/=]", function(c)
return string.format("=%02X", string.byte(c))
end)
end
function write_file(filename, contents) function write_file(filename, contents)
local f, err = io.open(filename, "w") local f, err = io.open(filename, "w")
if not f then if not f then

View File

@@ -53,7 +53,7 @@ local target = require "target"
local HOSTMAP_BING_SERVER = "www.ip2hosts.com" local HOSTMAP_BING_SERVER = "www.ip2hosts.com"
local HOSTMAP_DEFAULT_PROVIDER = "ALL" local HOSTMAP_DEFAULT_PROVIDER = "ALL"
local filename_escape, write_file local write_file
hostrule = function(host) hostrule = function(host)
return not ipOps.isPrivate(host.ip) return not ipOps.isPrivate(host.ip)
@@ -99,7 +99,7 @@ action = function(host)
output_tab.hosts = hostnames output_tab.hosts = hostnames
--write to file --write to file
if filename_prefix then if filename_prefix then
local filename = filename_prefix .. filename_escape(host.targetname or host.ip) local filename = filename_prefix .. stdnse.filename_escape(host.targetname or host.ip)
hostnames_str = stdnse.strjoin("\n", hostnames) hostnames_str = stdnse.strjoin("\n", hostnames)
local status, err = write_file(filename, hostnames_str) local status, err = write_file(filename, hostnames_str)
if status then if status then
@@ -112,13 +112,6 @@ action = function(host)
return output_tab return output_tab
end end
-- Escape some potentially unsafe characters in a string meant to be a filename.
function filename_escape(s)
return string.gsub(s, "[%z/=]", function(c)
return string.format("=%02X", string.byte(c))
end)
end
function write_file(filename, contents) function write_file(filename, contents)
local f, err = io.open(filename, "w") local f, err = io.open(filename, "w")
if not f then if not f then

View File

@@ -209,7 +209,7 @@ action = function (host, port)
if (response.status == 200) then if (response.status == 200) then
-- check it if is valid before inserting -- check it if is valid before inserting
if cfg.check(response.body) then if cfg.check(response.body) then
local filename = ((host.targetname or host.ip) .. url_path):gsub("/", "-"); local filename = stdnse.escape_filename((host.targetname or host.ip) .. url_path)
-- save the content -- save the content
if save then if save then

View File

@@ -315,9 +315,10 @@ action = function(host, port)
http_response = http.get( vhost or host, port, u_details.idfile, { auth = { username = user, password = pass }, no_cache = true }) http_response = http.get( vhost or host, port, u_details.idfile, { auth = { username = user, password = pass }, no_cache = true })
if ( http_response.status == 200 ) then if ( http_response.status == 200 ) then
local status, err = saveIDFile( ("%s/%s.id"):format(download_path, u_details.fullname), http_response.body ) local filename = download_path .. "/" .. stdnse.filename_escape(u_details.fullname .. ".id")
local status, err = saveIDFile( filename, http_response.body )
if ( status ) then if ( status ) then
table.insert( id_files, ("%s ID File has been downloaded (%s/%s.id)"):format(u_details.fullname, download_path, u_details.fullname) ) table.insert( id_files, ("%s ID File has been downloaded (%s)"):format(u_details.fullname, filename) )
else else
table.insert( id_files, ("%s ID File was not saved (error: %s)"):format(u_details.fullname, err ) ) table.insert( id_files, ("%s ID File was not saved (error: %s)"):format(u_details.fullname, err ) )
end end

View File

@@ -119,7 +119,7 @@ action = function( host, port )
local filename local filename
if ( dir ) then if ( dir ) then
local instance = instance:GetName():match("%\\+(.+)$") or instance:GetName() local instance = instance:GetName():match("%\\+(.+)$") or instance:GetName()
filename = ("%s/%s_%s_ms-sql_hashes.txt"):format(dir, host.ip, instance) filename = dir .. "/" .. stdnse.filename_escape(("%s_%s_ms-sql_hashes.txt"):format(host.ip, instance))
saveToFile(filename, instanceOutput[1]) saveToFile(filename, instanceOutput[1])
end end
end end

View File

@@ -184,7 +184,7 @@ action = function(host, port)
result = ( infile and infile:getContent() ) result = ( infile and infile:getContent() )
if ( tftproot ) then if ( tftproot ) then
local fname = tftproot .. host.ip .. "-config" local fname = tftproot .. stdnse.filename_escape(host.ip .. "-config")
local file, err = io.open(fname, "w") local file, err = io.open(fname, "w")
if ( file ) then if ( file ) then
file:write(result) file:write(result)

View File

@@ -81,7 +81,7 @@ local function check_infected(host, path, save)
fmt = save:gsub("%%h", host.ip) fmt = save:gsub("%%h", host.ip)
fmt = fmt:gsub("%%v", version) fmt = fmt:gsub("%%v", version)
file = io.open(fmt, "w") file = io.open(stdnse.filename_escape(fmt), "w")
if file then if file then
stdnse.print_debug(1, "Wrote %d bytes to file %s.", #result.arguments, fmt) stdnse.print_debug(1, "Wrote %d bytes to file %s.", #result.arguments, fmt)
file:write(result.arguments) file:write(result.arguments)