Escape XML special characters in OMP2 authenticate() credentials

Made-with: Cursor
This commit is contained in:
sachin9967 2026-03-14 12:06:52 +05:30
parent aea57f7849
commit 54df94d99f

View file

@ -13,15 +13,16 @@
-- website: http://www.openvas.org/omp-2-0.html
--
-- Sample use:
-- <code>
-- local session = omp2.Session:new()
-- local status, err = session:connect(host, port)
-- local status, err = session:authenticate(username, password)
-- ...
-- session:close()
-- </code>
--
-- local session = omp2.Session:new()
-- local status, err = session:connect(host, port)
-- local status, err = session:authenticate(username, password)
-- ...
-- session:close()
--
--
-- @author Henri Doreau
-- @author Sachin
-- @copyright Same as Nmap -- See https://nmap.org/book/man-legal.html
--
-- @args omp2.username The username to use for authentication.
@ -31,150 +32,165 @@
local nmap = require "nmap"
local stdnse = require "stdnse"
local table = require "table"
-- Escape XML special characters
local function xml_escape(s)
if not s then return "" end
s = s:gsub("&", "&amp;")
s = s:gsub("<", "&lt;")
s = s:gsub(">", "&gt;")
s = s:gsub("\"", "&quot;")
s = s:gsub("'", "&apos;")
return s
end
_ENV = stdnse.module("omp2", stdnse.seeall)
local HAVE_SSL = false
if pcall(require,'openssl') then
HAVE_SSL = true
HAVE_SSL = true
end
--- A Session class holds connection and interaction with the server
Session = {
--- Creates a new session object
new = function(self, socket)
--- Creates a new session object
new = function(self, socket)
local o = {}
setmetatable(o, self)
self.__index = self
local o = {}
setmetatable(o, self)
self.__index = self
o.username = nmap.registry.args["omp2.username"]
o.password = nmap.registry.args["omp2.password"]
o.socket = socket or nmap.new_socket()
o.username = nmap.registry.args["omp2.username"]
o.password = nmap.registry.args["omp2.password"]
o.socket = socket or nmap.new_socket()
return o
end,
return o
end,
--- Establishes the (SSL) connection to the remote server
connect = function(self, host, port)
if not HAVE_SSL then
return false, "The OMP2 module requires OpenSSL support"
end
--- Establishes the (SSL) connection to the remote server
connect = function(self, host, port)
if not HAVE_SSL then
return false, "The OMP2 module requires OpenSSL support"
end
return self.socket:connect(host, port, "ssl")
end,
return self.socket:connect(host, port, "ssl")
end,
--- Closes connection
close = function(self)
return self.socket:close()
end,
--- Closes connection
close = function(self)
return self.socket:close()
end,
--- Attempts to authenticate on the current connection
authenticate = function(self, username, password)
local status, err, xmldata
--- Attempts to authenticate on the current connection
authenticate = function(self, username, password)
local status, err, xmldata
-- TODO escape credentials
status, err = self.socket:send("<authenticate><credentials>"
.. "<username>" .. username .. "</username>"
.. "<password>" .. password .. "</password>"
.. "</credentials></authenticate>")
-- Escape credentials to prevent malformed XML
local safe_username = xml_escape(username)
local safe_password = xml_escape(password)
if not status then
stdnse.debug1("ERROR: %s", err)
return false, err
end
status, err = self.socket:send("<authenticate><credentials>"
.. "<username>" .. safe_username .. "</username>"
.. "<password>" .. safe_password .. "</password>"
.. "</credentials></authenticate>")
status, xmldata = self.socket:receive()
if not status then
stdnse.debug1("ERROR: %s", xmldata)
return false, xmldata
end
if not status then
stdnse.debug1("ERROR: %s", err)
return false, err
end
return xmldata:match('status="200"')
end,
status, xmldata = self.socket:receive()
if not status then
stdnse.debug1("ERROR: %s", xmldata)
return false, xmldata
end
--- Lists targets defined on the remote server
ls_targets = function(self)
local status, err, xmldata
local res, target_names, target_hosts = {}, {}, {}
return xmldata:match('status="200"')
end,
status, err = self.socket:send("<get_targets/>")
--- Lists targets defined on the remote server
ls_targets = function(self)
local status, err, xmldata
local res, target_names, target_hosts = {}, {}, {}
if not status then
stdnse.debug1("ERROR: %s", err)
return false, err
end
status, err = self.socket:send("<get_targets/>")
status, xmldata = self.socket:receive()
if not status then
stdnse.debug1("ERROR: %s", xmldata)
return false, xmldata
end
if not status then
stdnse.debug1("ERROR: %s", err)
return false, err
end
-- As NSE has no XML parser yet, we use regexp to extract the data from the
-- XML output. Targets are defined as a name and the corresponding host(s).
-- Thus we gather both and return an associative array, using names as keys
-- and hosts as values.
status, xmldata = self.socket:receive()
if not status then
stdnse.debug1("ERROR: %s", xmldata)
return false, xmldata
end
local i = 0
for name in xmldata:gmatch("<name>(.-)</name>") do
-- XXX this is hackish: skip the second and third "<name>" tags, as they
-- describe other components than the targets.
-- see: http://www.openvas.org/omp-2-0.html#command_get_targets
if i % 3 == 0 then
table.insert(target_names, name)
end
i = i + 1
end
-- As NSE has no XML parser yet, we use regexp to extract the data from the
-- XML output. Targets are defined as a name and the corresponding host(s).
-- Thus we gather both and return an associative array, using names as keys
-- and hosts as values.
for hosts in xmldata:gmatch("<hosts>(.-)</hosts>") do
table.insert(target_hosts, hosts)
end
local i = 0
for name in xmldata:gmatch("<name>(.-)</name>") do
-- XXX this is hackish: skip the second and third "name" tags, as they
-- describe other components than the targets.
-- see: http://www.openvas.org/omp-2-0.html#command_get_targets
if i % 3 == 0 then
table.insert(target_names, name)
end
i = i + 1
end
for i, _ in ipairs(target_names) do
res[target_names[i]] = target_hosts[i]
end
for hosts in xmldata:gmatch("<hosts>(.-)</hosts>") do
table.insert(target_hosts, hosts)
end
return res
end,
for i, _ in ipairs(target_names) do
res[target_names[i]] = target_hosts[i]
end
return res
end,
}
--- Registers OMP2 credentials for a given host
function add_account(host, username, password)
if not nmap.registry[host.ip] then
nmap.registry[host.ip] = {}
end
if not nmap.registry[host.ip] then
nmap.registry[host.ip] = {}
end
if not nmap.registry[host.ip]["omp2accounts"] then
nmap.registry[host.ip]["omp2accounts"] = {}
end
if not nmap.registry[host.ip]["omp2accounts"] then
nmap.registry[host.ip]["omp2accounts"] = {}
end
table.insert(nmap.registry[host.ip]["omp2accounts"], {["username"] = username, ["password"] = password})
table.insert(nmap.registry[host.ip]["omp2accounts"], {["username"] = username, ["password"] = password})
end
--- Retrieves the list of accounts for a given host
function get_accounts(host)
local accounts = {}
local username, password
local accounts = {}
local username, password
username = nmap.registry.args["omp2.username"]
password = nmap.registry.args["omp2.password"]
username = nmap.registry.args["omp2.username"]
password = nmap.registry.args["omp2.password"]
if username and password then
table.insert(accounts, {["username"] = username, ["password"] = password})
end
if username and password then
table.insert(accounts, {["username"] = username, ["password"] = password})
end
if nmap.registry[host.ip] and nmap.registry[host.ip]["omp2accounts"] then
for _, account in pairs(nmap.registry[host.ip]["omp2accounts"]) do
table.insert(accounts, account)
end
end
if nmap.registry[host.ip] and nmap.registry[host.ip]["omp2accounts"] then
for _, account in pairs(nmap.registry[host.ip]["omp2accounts"]) do
table.insert(accounts, account)
end
end
if #accounts > 0 then
return accounts
end
return nil
if #accounts > 0 then
return accounts
end
return nil
end