Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions lib/msf/core/post/linux/kernel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@ def kernel_release
uname('-r')
end

#
# Returns the kernel release as a Rex::Version object.
#
# @param release [String, nil] Pre-fetched kernel release string.
# @return [Rex::Version] the upstream kernel version
# @return [nil] if the release could not be determined or parsed
#
def kernel_rex_version(release = nil)
release ||= kernel_release
return nil if release.blank?

version_string = release.split('-').first
return nil if version_string.blank?

Rex::Version.new(version_string)
rescue ArgumentError, RuntimeError
nil
end

#
# Returns the kernel version
#
Expand Down
14 changes: 6 additions & 8 deletions modules/exploits/example_linux_priv_esc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,13 @@ def base_dir

def check
# Check the kernel version to see if its in a vulnerable range
# we guard this because some distros have funky kernel versions https://github.com/rapid7/metasploit-framework/issues/19812
release = kernel_release
begin
if Rex::Version.new(release.split('-').first) > Rex::Version.new('4.14.11') ||
Rex::Version.new(release.split('-').first) < Rex::Version.new('4.0')
return CheckCode::Safe("Kernel version #{release} is not vulnerable")
end
rescue ArgumentError => e
return CheckCode::Safe("Error determining or processing kernel release (#{release}) into known format: #{e}")
v = kernel_rex_version(release)
if v.nil?
return CheckCode::Unknown("Could not determine kernel version from release string: #{release}")
end
if v > Rex::Version.new('4.14.11') || v < Rex::Version.new('4.0')
return CheckCode::Safe("Kernel version #{release} is not vulnerable")
end
vprint_good "Kernel version #{release} appears to be vulnerable"

Expand Down
5 changes: 4 additions & 1 deletion modules/exploits/linux/local/apport_abrt_chroot_priv_esc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ def check
end
vprint_good 'Unprivileged user namespaces are permitted'

kernel_version = Rex::Version.new kernel_release.split('-').first
kernel_version = kernel_rex_version
if kernel_version.nil?
return CheckCode::Unknown('Could not determine kernel version')
end

if kernel_version < Rex::Version.new('3.12')
vprint_error "Linux kernel version #{kernel_version} is not vulnerable"
Expand Down
8 changes: 6 additions & 2 deletions modules/exploits/linux/local/bpf_priv_esc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,12 @@ def check
release = kernel_release
version = kernel_version

if Rex::Version.new(release.split('-').first) < Rex::Version.new('4.4') ||
Rex::Version.new(release.split('-').first) > Rex::Version.new('4.5.5')
v = kernel_rex_version(release)
if v.nil?
return CheckCode::Unknown("Could not determine kernel version from release string: #{release}")
end
if v < Rex::Version.new('4.4') ||
v > Rex::Version.new('4.5.5')
vprint_error "Kernel version #{release} #{version} is not vulnerable"
return CheckCode::Safe
end
Expand Down
7 changes: 5 additions & 2 deletions modules/exploits/linux/local/bpf_sign_extension_priv_esc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,11 @@ def check
vprint_good("System architecture #{arch} is supported")

release = kernel_release
if Rex::Version.new(release.split('-').first) > Rex::Version.new('4.14.11') ||
Rex::Version.new(release.split('-').first) < Rex::Version.new('4.0')
v = kernel_rex_version(release)
if v.nil?
return CheckCode::Unknown("Could not determine kernel version from release string: #{release}")
end
if v > Rex::Version.new('4.14.11') || v < Rex::Version.new('4.0')
return CheckCode::Safe("Kernel version #{release} is not vulnerable")
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ def check
release = kernel_release
version = kernel_version

v = kernel_rex_version(release)
if v.nil?
return CheckCode::Unknown("Could not determine kernel version from release string: #{release}")
end

# If the target is Ubuntu...
if version =~ /[uU]buntu/
version_array = release.split('-')
Expand All @@ -113,13 +118,13 @@ def check

# First check if we are past the 5.11.x kernel releases and into at the time of
# writing beta versions of Ubuntu. If so,the target isn't vuln.
if Rex::Version.new(major_version) >= Rex::Version.new('5.12.0')
if v >= Rex::Version.new('5.12.0')
return CheckCode::Safe("Target Ubuntu kernel version is #{major_version}-#{minor_version} which is not vulnerable!")
elsif (Rex::Version.new(major_version) == Rex::Version.new('5.11.0')) && (Rex::Version.new(minor_version) >= Rex::Version.new('17.18'))
elsif (v == Rex::Version.new('5.11.0')) && (Rex::Version.new(minor_version) >= Rex::Version.new('17.18'))
return CheckCode::Safe('Target Ubuntu kernel version is running a 5.11.x build however it has updated to a patched version!')
elsif (Rex::Version.new(major_version) == Rex::Version.new('5.8.0')) && (Rex::Version.new(minor_version) >= Rex::Version.new('53.60'))
elsif (v == Rex::Version.new('5.8.0')) && (Rex::Version.new(minor_version) >= Rex::Version.new('53.60'))
return CheckCode::Safe('Target Ubuntu kernel version is running a 5.8.x build however it has updated to a patched version!')
elsif (Rex::Version.new(major_version) != Rex::Version.new('5.8.0')) && (Rex::Version.new(major_version) != Rex::Version.new('5.11.0')) # Only Ubuntu 20.04.02, Groovy, and Hirsuite are affected, other releases used a kernel either too old or which was patched.
elsif (v != Rex::Version.new('5.8.0')) && (v != Rex::Version.new('5.11.0')) # Only Ubuntu 20.04.02, Groovy, and Hirsuite are affected, other releases used a kernel either too old or which was patched.
return CheckCode::Unknown('Unknown target kernel version, recommend manually checking if target kernel is vulnerable.')
end
elsif release =~ /\.fc3[2,3,4]\./
Expand All @@ -132,11 +137,11 @@ def check
fail_with(Failure::UnexpectedReply, 'The target Fedora server does not have the expected minor kernel version format!')
end
minor_version = version_array[1].split('.')[0]
if Rex::Version.new(major_version) >= Rex::Version.new('5.11.20')
if v > Rex::Version.new('5.11.20')
return CheckCode::Safe("Target Fedora kernel version is #{major_version}-#{minor_version} which is not vulnerable!")
elsif Rex::Version.new(major_version) == Rex::Version.new('5.11.20') && Rex::Version.new(minor_version) >= Rex::Version.new('300')
elsif v == Rex::Version.new('5.11.20') && Rex::Version.new(minor_version) >= Rex::Version.new('300')
return CheckCode::Safe('Target Fedora system is running a 5.11.20 kernel however it has been patched!')
elsif Rex::Version.new(major_version) <= Rex::Version.new('5.7')
elsif v <= Rex::Version.new('5.7')
return CheckCode::Safe('Running a Fedora system with a kernel before kernel version 5.7 where the vulnerability was introduced')
end
else
Expand Down
5 changes: 4 additions & 1 deletion modules/exploits/linux/local/cve_2022_0847_dirtypipe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ def check
return CheckCode::Safe("System architecture #{arch} is not supported without live compilation")
end

kernel_version = Rex::Version.new kernel_release.split('-').first
kernel_version = kernel_rex_version
if kernel_version.nil?
return CheckCode::Unknown('Could not determine kernel version')
end
if kernel_version < Rex::Version.new('5.8') ||
kernel_version >= Rex::Version.new('5.16.11') ||
(kernel_version >= Rex::Version.new('5.15.25') && kernel_version < Rex::Version.new('5.16')) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,12 @@ def base_dir
def check
# Check the kernel version to see if its in a vulnerable range
release = kernel_release
if Rex::Version.new(release.split('-').first) > Rex::Version.new('5.14-rc7') ||
Rex::Version.new(release.split('-').first) < Rex::Version.new('5.12-rc3')
v = kernel_rex_version(release)
if v.nil?
return CheckCode::Unknown("Could not determine kernel version from release string: #{release}")
end

if v > Rex::Version.new('5.14-rc7') || v < Rex::Version.new('5.12-rc3')
vprint_error "Kernel version #{release} is not vulnerable"
return CheckCode::Safe
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ def check
return CheckCode::Safe("System architecture #{kernel_arch} is not supported")
end

kernel_version = Rex::Version.new(kernel_release.split('-').first)
kernel_version = kernel_rex_version
if kernel_version.nil?
return CheckCode::Unknown('Could not determine kernel version')
end
if kernel_version < Rex::Version.new('5.11') ||
kernel_version.between?(Rex::Version.new('5.15.91'), Rex::Version.new('5.16')) ||
Rex::Version.new('6.1.9') <= kernel_version
Expand Down
22 changes: 13 additions & 9 deletions modules/exploits/linux/local/docker_cgroup_escape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,21 @@ def check
print_status('Unable to determine host OS, this check method is unlikely to be accurate if the host isn\'t Ubuntu')
release = kernel_release
# https://people.canonical.com/~ubuntu-security/cve/2022/CVE-2022-0492
release_short = kernel_rex_version(release)
if release_short.nil?
return CheckCode::Unknown("Could not determine kernel version from release string: #{release}")
end

begin
release_short = Rex::Version.new(release.split('-').first)
release_long = Rex::Version.new(release.split('-')[0..1].join('-'))
if release_short >= Rex::Version.new('5.13.0') && release_long < Rex::Version.new('5.13.0-37.42') || # Ubuntu 21.10
release_short >= Rex::Version.new('5.4.0') && release_long < Rex::Version.new('5.4.0-105.119') || # Ubuntu 20.04 LTS
release_short >= Rex::Version.new('4.15.0') && release_long < Rex::Version.new('4.15.0-173.182') || # Ubuntu 18.04 LTS
release_short >= Rex::Version.new('4.4.0') && release_long < Rex::Version.new('4.4.0-222.255') # Ubuntu 16.04 ESM
return CheckCode::Vulnerable("IF host OS is Ubuntu, kernel version #{release} is vulnerable")
end
rescue ArgumentError => e
return CheckCode::Safe("Error determining or processing kernel release (#{release}) into known format: #{e}")
rescue ArgumentError
return CheckCode::Unknown("Could not parse extended kernel version from: #{release}")
end
if release_short >= Rex::Version.new('5.13.0') && release_long < Rex::Version.new('5.13.0-37.42') || # Ubuntu 21.10
release_short >= Rex::Version.new('5.4.0') && release_long < Rex::Version.new('5.4.0-105.119') || # Ubuntu 20.04 LTS
release_short >= Rex::Version.new('4.15.0') && release_long < Rex::Version.new('4.15.0-173.182') || # Ubuntu 18.04 LTS
release_short >= Rex::Version.new('4.4.0') && release_long < Rex::Version.new('4.4.0-222.255') # Ubuntu 16.04 ESM
return CheckCode::Vulnerable("IF host OS is Ubuntu, kernel version #{release} is vulnerable")
end

CheckCode::Safe("Kernel version #{release} may not be vulnerable depending on the host OS")
Expand Down
6 changes: 5 additions & 1 deletion modules/exploits/linux/local/glibc_realpath_priv_esc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,11 @@ def live_compile?

def check
version = kernel_release
if Rex::Version.new(version.split('-').first) < Rex::Version.new('2.6.36')
v = kernel_rex_version(version)
if v.nil?
return CheckCode::Unknown("Could not determine kernel version from release string: #{version}")
end
if v < Rex::Version.new('2.6.36')
vprint_error "Linux kernel version #{version} is not vulnerable"
return CheckCode::Safe
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,10 @@ def check

# Patched in 4.18.19 and 4.19.2
release = kernel_release
v = Rex::Version.new release.split('-').first
v = kernel_rex_version(release)
if v.nil?
return CheckCode::Unknown("Could not determine kernel version from release string: #{release}")
end
if v < Rex::Version.new('4.15') ||
v >= Rex::Version.new('4.19.2') ||
(v >= Rex::Version.new('4.18.19') && v < Rex::Version.new('4.19'))
Expand Down
5 changes: 4 additions & 1 deletion modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ def check
# Introduced in 4.10, but also backported
# Patched in 4.4.185, 4.9.185, 4.14.133, 4.19.58, 5.1.17
release = kernel_release
v = Rex::Version.new release.split('-').first
v = kernel_rex_version(release)
if v.nil?
return CheckCode::Unknown("Could not determine kernel version from release string: #{release}")
end

if v >= Rex::Version.new('5.1.17') || v < Rex::Version.new('3')
vprint_error "Kernel version #{release} is not vulnerable"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,11 @@ def upload(path, data)

def check
version = kernel_release
unless Rex::Version.new(version.split('-').first) >= Rex::Version.new('2.6.30') &&
Rex::Version.new(version.split('-').first) < Rex::Version.new('2.6.37')
v = kernel_rex_version(version)
if v.nil?
return CheckCode::Unknown("Could not determine kernel version from release string: #{version}")
end
unless v >= Rex::Version.new('2.6.30') && v < Rex::Version.new('2.6.37')
return CheckCode::Safe("Linux kernel version #{version} is not vulnerable")
end

Expand Down
8 changes: 3 additions & 5 deletions modules/exploits/linux/local/sock_sendpage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,9 @@ def upload(path, data)
end

def check
version = Rex::Version.new kernel_release.split('-').first

if version.to_s.eql? ''
vprint_error 'Could not determine the kernel version'
return CheckCode::Unknown
version = kernel_rex_version
if version.nil?
return CheckCode::Unknown('Could not determine the kernel version')
end

if version.between?(Rex::Version.new('2.4.4'), Rex::Version.new('2.4.37.4')) ||
Expand Down
15 changes: 7 additions & 8 deletions modules/exploits/linux/local/vmwgfx_fd_priv_esc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def initialize(info = {})
a free'd 'file' object.

We use this bug to overwrite a SUID binary with our payload and gain root.
Linux kernel 4.14-rc1 - 5.17-rc1 are vulnerable.
Linux kernel 4.14 - 5.16.x are vulnerable.

Successfully tested against Ubuntu 22.04.01 with kernel 5.13.12-051312-generic.
},
Expand Down Expand Up @@ -74,13 +74,12 @@ def base_dir
def check
# Check the kernel version to see if its in a vulnerable range
release = kernel_release
begin
unless Rex::Version.new(release) > Rex::Version.new('4.14-rc1') &&
Rex::Version.new(release) < Rex::Version.new('5.17-rc1')
return CheckCode::Safe("Kernel version #{release} is not vulnerable")
end
rescue ArgumentError => e
return CheckCode::Safe("Error determining or processing kernel release (#{release}) into known format: #{e}")
v = kernel_rex_version(release)
if v.nil?
return CheckCode::Unknown("Could not determine kernel version from release string: #{release}")
end
unless v >= Rex::Version.new('4.14') && v < Rex::Version.new('5.17')
return CheckCode::Safe("Kernel version #{release} is not vulnerable")
end

vprint_good "Kernel version #{release} appears to be vulnerable"
Expand Down
64 changes: 64 additions & 0 deletions spec/lib/msf/core/post/linux/kernel_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,68 @@
end
end
end

describe '#kernel_rex_version' do
[
{ release: '5.15.0-25-generic', expected: '5.15.0', label: 'Ubuntu' },
{ release: '5.13.0-37.42', expected: '5.13.0', label: 'Ubuntu (docker cgroup)' },
{ release: '4.14.355-275.572.amzn2.x86_64', expected: '4.14.355', label: 'Amazon Linux 2' },
{ release: '5.4.129-72.229.amzn2int.x86_64', expected: '5.4.129', label: 'Amazon Linux 2 (int)' },
{ release: '4.0.4-301.fc22.x86_64', expected: '4.0.4', label: 'Fedora' },
{ release: '3.10.0-1160.el7.x86_64', expected: '3.10.0', label: 'RHEL/CentOS' },
{ release: '6.11.2-amd64', expected: '6.11.2', label: 'Debian' },
{ release: '6.6.7-arch1-1', expected: '6.6.7', label: 'Arch Linux' },
{ release: '5.4.0', expected: '5.4.0', label: 'simple version (no suffix)' },
{ release: '5.14.21-150500.55.83-default', expected: '5.14.21', label: 'SUSE/openSUSE' },
{ release: '6.6.63-0-lts', expected: '6.6.63', label: 'Alpine Linux' },
].each do |test_case|
context "with #{test_case[:label]} kernel (#{test_case[:release]})" do
it "returns Rex::Version #{test_case[:expected]}" do
allow(subject).to receive(:cmd_exec).and_return(test_case[:release])
result = subject.kernel_rex_version
expect(result).to be_a(Rex::Version)
expect(result).to eq(Rex::Version.new(test_case[:expected]))
end
end
end

context 'when a pre-fetched release string is provided' do
it 'uses the provided string instead of calling kernel_release' do
expect(subject).not_to receive(:cmd_exec)
result = subject.kernel_rex_version('5.15.0-25-generic')
expect(result).to eq(Rex::Version.new('5.15.0'))
end
end

context 'when kernel_release returns a blank string' do
it 'returns nil' do
allow(subject).to receive(:cmd_exec).and_return('')
expect(subject.kernel_rex_version).to be_nil
end
end

context 'when kernel_release returns nil' do
it 'returns nil' do
allow(subject).to receive(:cmd_exec).and_return(nil)
expect(subject.kernel_rex_version).to be_nil
end
end

context 'when the version string before the first hyphen is not parseable' do
it 'returns nil' do
result = subject.kernel_rex_version('xyz-not-a-version')
expect(result).to be_nil
end
end

context 'when version comparison is used' do
it 'allows comparison with other Rex::Version objects' do
allow(subject).to receive(:cmd_exec).and_return('5.15.0-25-generic')
v = subject.kernel_rex_version
expect(v).to be > Rex::Version.new('5.14.0')
expect(v).to be < Rex::Version.new('5.16.0')
expect(v).to eq(Rex::Version.new('5.15.0'))
end
end
end
end
Loading