Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
http-vuln-exchange.nse
======================

This script, initially created by Kevin Beaumont (a.k.a @GossiTheDog) tries to determine the patch status of Exchange servers against the CVEs used by Hafnium (a.k.a. ProxyLogin a.k.a. - CVE-2021-26855, CVE-2021-26857, CVE-2021-26858 and CVE-2021-27065) by looking at the version number found on the /owa URI.

Last updated on 19-3-2021

Version/patch information based on:
* https://techcommunity.microsoft.com/t5/exchange-team-blog/released-march-2021-exchange-server-security-updates/ba-p/2175901
* https://support.microsoft.com/en-us/topic/description-of-the-security-update-for-microsoft-exchange-server-2019-2016-and-2013-march-2-2021-kb5000871-9800a6bb-0a21-4ee7-b9da-fa85b3e1d23b
* https://docs.microsoft.com/en-us/exchange/new-features/build-numbers-and-release-dates?view=exchserver-2019
97 changes: 75 additions & 22 deletions http-vuln-exchange.nse
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ Originally based on source by onSec-fr and k4nfr3, thanks!
--443/tcp open https
--|_http-vuln-proxylogon: (15.1.2176) Exchange 2016 potentially vulnerable, check latest security update is applied (Exchange 2016 CU18 or CU19 installed)

author = "Kevin Beaumont"
author = "Kevin Beaumont / Dutch Institute for Vulnerability Disclosure (DIVD.nl)"
last_update = "March 19, 2021"
license = "GPLv3"
categories = {"default", "discovery", "safe", "exploit"}

Expand Down Expand Up @@ -49,43 +50,95 @@ local function checkversion(w)

elseif w:find("^14.0.*") ~= nil then
if tonumber(mytable[3]) < 727 then
output = "Exchange 2010 VULNERABLE to Unified Messaging issues! (< 14 RTM version installed, no Service Packs)"
output = "Exchange 2010 VULNERABLE! to Unified Messaging issues! (< 14 RTM version installed, no Service Packs)"
end

elseif w:find("^14.*") ~= nil then
if tonumber(mytable[3]) < 496 then
output = "Exchange 2010 VULNERABLE to Unified Messaging issues! (< 14.*.496)"
output = "Exchange 2010 VULNERABLE! to Unified Messaging issues! (< 14.*.496)"
elseif tonumber(mytable[3]) == 496 then
output = "Exchange 2010 potentially vulnerable, check latest security update is applied (= 14.*.496)"
output = "Exchange 2010 patch status cannot be determined from version number, check locally if latest security update is applied (= 14.*.496)"
else
output = "Exchange 2010 not vulnerable (>14.*.496)"
output = "Exchange 2010 PATCHED (>14.*.496)"
end

--Exchange 2013 - Patches available for CU23 (1497), CU22 (1473), CU21 (1395) and SP1 (847)

elseif w:find("^15.0.*") ~= nil then
if tonumber(mytable[3]) < 1497 then
output = "Exchange 2013 VULNERABLE! (< 15.0.1496)"
elseif tonumber(mytable[3]) == 1497 then
output = "Exchange 2013 potentially vulnerable, check latest security update is applied (15.0.1497 Exchange 2013 CU23 installed)"
minor = tonumber(mytable[3])
if minor == 1497 then
output = "Exchange 2013 CU23. Patch status cannot be determined from version number, check locally if patches are applied (= 15.0." .. minor .. ")"
elseif minor == 1473 then
output = "Exchange 2013 CU22. Patch status cannot be determined from version number, check locally if patches are applied (= 15.0." .. minor .. ")"
elseif minor == 1395 then
output = "Exchange 2013 CU21. Patch status cannot be determined from version number, check locally if patches are applied (= 15.0." .. minor .. ")"
elseif minor - 847 then
output = "Exchange 2013 SP1. Patch status cannot be determined from version number, check locally if patches are applied (= 15.0." .. minor .. ")"
elseif minor > 1497 then
output = "Exchange 2013 after CU23. PATCHED (> 15.0.1497)"
else
output = "Exchange 2013 not vulnerable (>15.0.1497)"
output = "Exchange 2013. Likely VULNERABLE! No security patches available on " .. last_update .. "(!~ 15.0.(1497|1473|1395|847) and < 15.0.1497)"
end

-- Exchange 2016 - Patches available for CU19 (2176), CU18 (2106), CU17 (2044), CU16 (1979), CU15 (1913), CU14 (1847), CU13 (1779), CU12 (1713), CU11 (1591),
-- CU10 (1531), CU9 (1466), CU8 (1415)

elseif w:find("^15.1.*") ~= nil then
if tonumber(mytable[3]) == 2176 or tonumber(mytable[3]) == 2106 then
output = "Exchange 2016 potentially vulnerable, check latest security update is applied (Exchange 2016 CU18 or CU19 installed)"
elseif tonumber(mytable[3]) < 2106 then
output = "Exchange 2016 VULNERABLE! (< 15.1.2106)"
minor = tonumber(mytable[3])
if minor == 2176 then
output = "Exchange 2016 CU19. Patch status cannot be determined from version number, check locally if patches are applied (= 15.1." .. minor .. ")"
elseif minor == 2106 then
output = "Exchange 2016 CU18. Patch status cannot be determined from version number, check locally if patches are applied (= 15.1." .. minor .. ")"
elseif minor == 2044 then
output = "Exchange 2016 CU17. Patch status cannot be determined from version number, check locally if patches are applied (= 15.1." .. minor .. ")"
elseif minor == 1979 then
output = "Exchange 2016 CU16. Patch status cannot be determined from version number, check locally if patches are applied (= 15.1." .. minor .. ")"
elseif minor == 1913 then
output = "Exchange 2016 CU15. Patch status cannot be determined from version number, check locally if patches are applied (= 15.1." .. minor .. ")"
elseif minor == 1847 then
output = "Exchange 2016 CU14. Patch status cannot be determined from version number, check locally if patches are applied (= 15.1." .. minor .. ")"
elseif minor == 1779 then
output = "Exchange 2016 CU13. Patch status cannot be determined from version number, check locally if patches are applied (= 15.1." .. minor .. ")"
elseif minor == 1713 then
output = "Exchange 2016 CU12. Patch status cannot be determined from version number, check locally if patches are applied (= 15.1." .. minor .. ")"
elseif minor == 1591 then
output = "Exchange 2016 CU11. Patch status cannot be determined from version number, check locally if patches are applied (= 15.1." .. minor .. ")"
elseif minor == 1531 then
output = "Exchange 2016 CU10. Patch status cannot be determined from version number, check locally if patches are applied (= 15.1." .. minor .. ")"
elseif minor == 1466 then
output = "Exchange 2016 CU9. Patch status cannot be determined from version number, check locally if patches are applied (= 15.1." .. minor .. ")"
elseif minor == 1415 then
output = "Exchange 2016 CU8. Patch status cannot be determined from version number, check locally if patches are applied (= 15.1." .. minor .. ")"
elseif minor > 2176 then
output = "Exchange 2016 after CU19. PATCHED (> 15.1.2176)"
else
output = "Exchange 2016 not vulnerable (> 15.1.2176)"
output = "Exchange 2016 before CU8. VULNERABLE! No patches available on " .. last_update .. "(< 15.1.1415)"
end

-- Exchange 2019 - Patches available for CU8 (792), CU7 (721), CU6 (659), CU5 (595), CU4 (529), CU3 (464), CU2 (397), CU1 (221)

elseif w:find("^15.2.*") ~= nil then
if tonumber(mytable[3]) == 792 or tonumber(mytable[3]) == 721 then
output = "Exchange 2019 potentially vulnerable, check latest security update is applied (Exchange 2019 CU7 or CU8 installed)"
elseif tonumber(mytable[3]) < 720 then
output = "Exchange 2019 VULNERABLE !!! (< 15.2.720)"
minor = tonumber(mytable[3])
if minor == 792 then
output = "Exchange 2019 CU8. Patch status cannot be determined from version number, check locally if patches are applied (= 15.2." .. minor .. ")"
elseif minor == 721 then
output = "Exchange 2019 CU7. Patch status cannot be determined from version number, check locally if patches are applied (= 15.2." .. minor .. ")"
elseif minor == 659 then
output = "Exchange 2019 CU6. Patch status cannot be determined from version number, check locally if patches are applied (= 15.2." .. minor .. ")"
elseif minor == 595 then
output = "Exchange 2019 CU5. Patch status cannot be determined from version number, check locally if patches are applied (= 15.2." .. minor .. ")"
elseif minor == 529 then
output = "Exchange 2019 CU4. Patch status cannot be determined from version number, check locally if patches are applied (= 15.2." .. minor .. ")"
elseif minor == 464 then
output = "Exchange 2019 CU3. Patch status cannot be determined from version number, check locally if patches are applied (= 15.2." .. minor .. ")"
elseif minor == 397 then
output = "Exchange 2019 CU2. Patch status cannot be determined from version number, check locally if patches are applied (= 15.2." .. minor .. ")"
elseif minor == 221 then
output = "Exchange 2019 CU1. Patch status cannot be determined from version number, check locally if patches are applied (= 15.2." .. minor .. ")"
elseif minor > 792 then
output = "Exchange 2019 after CU8, PATCHED (> 15.2.792)"
else
output = "Exchange 2019 not vulnerable (> 15.2.792)"
output = "Exchange 2019 before CU1. VULNERABLE! No patches available on " .. last_update .. " (< 15.2.221)"
end
else
output = "Exchange " .. w
Expand Down Expand Up @@ -114,15 +167,15 @@ local function parse_answer(body)
end
end

action = function(host, port)
action = function(host, port, redirects)
local dis_count, noun
options = {header={}} options['header']['User-Agent'] = "Mozilla/5.0 (Exchange check)"
local answer = http.get(host, port, "/owa", options )

if answer.status == 302 then
return "Error 302 " .. answer.location
elseif answer.status ~= 200 then
return "Error " .. tostring(answer.status) .. " for /owa"
return "Error: " .. tostring(answer["status-line"]) .. " for /owa"
end

local v_level = nmap.verbosity() + (nmap.debugging()*2)
Expand Down