diff --git a/nselib/omp2.lua b/nselib/omp2.lua index 83c38cdd2..bd64be146 100644 --- a/nselib/omp2.lua +++ b/nselib/omp2.lua @@ -13,15 +13,16 @@ -- website: http://www.openvas.org/omp-2-0.html -- -- Sample use: --- --- local session = omp2.Session:new() --- local status, err = session:connect(host, port) --- local status, err = session:authenticate(username, password) --- ... --- session:close() --- +-- +-- 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("&", "&") + s = s:gsub("<", "<") + s = s:gsub(">", ">") + s = s:gsub("\"", """) + s = s:gsub("'", "'") + 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("" - .. "" .. username .. "" - .. "" .. password .. "" - .. "") + -- 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("" + .. "" .. safe_username .. "" + .. "" .. safe_password .. "" + .. "") - 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("") + --- 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("") - 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("(.-)") do - -- XXX this is hackish: skip the second and third "" 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("(.-)") do - table.insert(target_hosts, hosts) - end + local i = 0 + for name in xmldata:gmatch("(.-)") 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("(.-)") 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