对PHP-FPM RCE漏洞(CVE-2019-11043)的调试和分析

2019-10-31 约 88 字 预计阅读 1 分钟

声明:本文 【对PHP-FPM RCE漏洞(CVE-2019-11043)的调试和分析】 由作者 kuron3k0 于 2019-10-31 09:15:32 首发 先知社区 曾经 浏览数 129 次

感谢 kuron3k0 的辛苦付出!

本来只是想验证一下漏洞,但是Emil Lerner大佬的EXP一直用不了,LoRexxar大佬的分析省略了一些关键信息也看不懂(好吧主要还是太菜了),只能自己动手调一遍了,还好90sec的maple大佬分析的很详细,不然感觉调不出来。。

0x00 漏洞成因

万恶之源,就是Nginx处理url的正则有个bug,在解析的时候把path_info置空了

0x01 漏洞利用

首先是Nginx这个点,加了换行然后就匹配不了,很像之前CTF题里面的情况,于是也下了Nginx源码调试了一下,发现正则处理是调用的libpcre.so,PHP调用正则时也是用的这个库

这里直接用PHP来模拟Nginx的处理过程。这是不带%0A的字符串,可以正常匹配

%0A在字符串中间的时候,libpcre会直接不匹配。

在结尾的时候又可以匹配了。

具体实现可能跟pcre正则的状态机有关,这里就不再深入了。

开始调PHP-FPM,这里我下载的是7.3.10的版本的源码,为了进行调试,编译的时候需要加上enable debug的选项

./configure --prefix=/root/php7.3.10 --enable-phpdbg-debug --enable-debug --enable-fpm CFLAGS="-g3 -gdwarf-4"

Nginx的配置

location ~ [^/]\.php(/|$) {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        include fastcgi_params;

        fastcgi_param PATH_INFO       $fastcgi_path_info;
        fastcgi_index index.php;
        fastcgi_param  REDIRECT_STATUS    200;
        fastcgi_param  SCRIPT_FILENAME /opt/nginx-src/nginx-branches-stable-1.10/html$fastcgi_script_name;
        fastcgi_param  DOCUMENT_ROOT /opt/nginx-src/nginx-branches-stable-1.10/html;
        fastcgi_pass 127.0.0.1:9000;
    }

一切准备就绪之后,用gdb连上php-fpm,漏洞函数init_request_info打上断点,然后在burp发包,现在已经进到这个函数

获取Nginx传过来的PATH_INFO参数,可以看到是值是空的

这里涉及到几个参数

path_info赋值,因为env_path_info指针的指向虽然是空的,但env_path_info的值是指针的地址,而且pilen是0,所以path_info得到的是env_path_info指针向上偏移slen的值

这里有个关键的点就是fcgi_hash_bucketfcgi_hash_seg这两个结构。

fcgi_hash_bucket是一个哈希表,保存着请求的环境变量的键值对:

fcgi_hash_seg就是保存这些键值对的地方,前面是3个8字节的指针pos(下一个要插入的变量的位置)、end(当前fcgi_hash_segdata块的终点)、next(下一个fcgi_hash_seg的位置),后面的data就是连续的键值对,fcgi_hash_bucket中的指向就在这里:

当一个fcgi_hash_seg到达一定大小后,再插入变量会重新分配一个fcgi_hash_seg,所以通过调整其他参数,可以看到前面提到的env_path_info刚好是位于一个新块的开头

path_info指针减去了len('PHP_VALUE%0Aauto_prepend_file=a;;;;')之后,刚好指向了fcgi_hash_segpos指针的最低位

接着把这一位置零,置零前

置零后

跟进FCGI_PUTENV函数

进入fcgi_hash_set函数,这里向前面提到的fcgi_hash_segpos指针指向进行了两次写操作,第一次是写参数名,第二次才是要写进去的php环境变量

参数名写完之后,pos指针往后移了0x11,现在开始写PHP_VALUE

前面提到fcgi_hash_bucket是一个哈希表,所以php-fpm获取环境变量时也是根据哈希来获取,所以单单把PHP_VALUE写进去是不行的,获取的时候会判断哈希和长度,EXP的作者是fuzz出了跟它长度一样、hash也一样的变量HTTP_EBUT,最后是需要把PHP_VALUE覆盖到这个HTTP_EBUT上即可。由于长度可控,所以多余的字符不会影响变量读取。上一张图可以看到str开头并不是PHP_VALUEh->data->pos开头也不是HTTP_EBUT,那是因为str前面还有11个字节的/index.php/,因此h->data->pos还需要往后移11字节完成对齐,完成这个工作的,就是在它前面新添加的http头。


最后为了演示简单,我就只写了auto_prepend_file=a这个php变量,并在html目录下新建了a文件,最终完成攻击。

还是要感叹一下大佬的运气,这得多少巧合才能触发这个异常,(m )n

0x02 Reference

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now