技术干货 | phpinfo里面有什么?

admin 2022年5月12日23:51:37评论353 views字数 11975阅读39分55秒阅读模式

本公众号发布的文章均转载自互联网或经作者投稿授权的原创,文末已注明出处,其内容和图片版权归原网站或作者本人所有,并不代表安世加的观点,若有无意侵权或转载不当之处请联系我们处理,谢谢合作!


欢迎各位添加微信号:asj-jacky

加入安世加 交流群 和大佬们一起交流安全技术

在PHP中,我们往往通过phpinfo()函数(及可选选项)来检查配置设置和预定义变量,返回结果输出关于PHP的配置信息,其中包含了 PHP 编译选项、启用的扩展、PHP 版本、服务器信息和环境变量(如果编译为一个模块的话)、PHP环境变量、操作系统版本信息、path 变量、配置选项的本地值和主值、HTTP 头和PHP授权信息(License)。根据phpinfo()的可选选项,重点分析部分选项信息。

选项名称 选项描述
INFO_GENERAL 1 配置的命令行、 php.ini 的文件位置、建立的时间、Web 服务器、系统及更多其他信息
INFO_CREDITS 2 PHP 贡献者名单
INFO_CONFIGURATION 4 当前PHP指令的本地值和主值
INFO_MODULES 8 已加载的模块和模块相应的设置
INFO_ENVIRONMENT 16 环境变量信息
INFO_VARIABLES 32 显示所有来自 EGPCS (Environment, GET, POST, Cookie, Server) 的 预定义变量
INFO_LICENSE 64 PHP的LICENSE信息
INFO_ALL -1 显示以上所有信息,默认

一般信息(INFO_GENERAL)

版本

首先很明显就是PHP的版本了。截至写下这篇文章,PHP已经发布到PHP 8.0.3,但目前应用最广泛的仍然还是5和7版本了。PHP各个版本之间有很多特定的安全利用的trick,以下列举PHP 5到8版本之间与安全性的部分改动信息。

PHP5.2及以前

  • __autoload自动加载类,但只能一次调用;spl_autoload_register加载类,__autoload的实现

PHP5.3

  • 修复空字符截断
  • 新增全局变量__DIR__
  • phar://流包装,绕过后缀限制、文件操作触发反序列化
  • glob://绕过open_dir列目录

PHP5.4

  • 删除注册全局变量、魔术引号、和安全模式
  • 新增session.upload_progress.enabled,默认为1,可用来文件包含

PHP5.5

  • 废除preg_replace中可代码执行的e修饰符
  • 容易引发变量覆盖的函数import_request_variables()不可用

PHP5.6

  • 运算符...实现函数中支持可变数量的参数列表

PHP7.0

  • 移除script标签<script language="php"></script>、asp标签<% ... %><%= ... %>
  • 含十六进制字符串不再被认为是数字
  • assert()成为一种语言构造,而不是一个函数,意味着很多assert一句话都会失效
  • 移除preg_replace中可代码执行的e修饰符

PHP7.1

  • 废除mb_ereg_replace()mb_eregi_replace()的e模式修饰符

PHP7.2

  • 禁用assert以字符串作为第一个参数
  • 废除可以动态执行字符串的create_function
  • 废除容易导致变量覆盖的parse_str()无设置参数的行为

PHP8.0

  • 字符串数字弱类型比较优化
  • assert()不再支持执行代码
  • 移除mb_ereg_replace()的e模式
  • 移除create_function
  • 移除php://filter中的string.strip_tags

这里仅仅列举各版本之间部分的改动信息,更多内容可在官方文档中进行查看。

操作系统版本和SAPI

技术干货 | phpinfo里面有什么?

操作系统版本信息可用于后续提权

SAPI可以明显地判断PHP的连接方式

Apache 2.0 Handler是通过Apache服务器的mod_php模块部署PHP服务的运行方式

技术干货 | phpinfo里面有什么?

FPM(FastCGI流程管理器)则是PHP FastCGI的一种替代实现。FPM有一个设计缺陷,由于两个进程间的通信没有进行安全性验证,可以伪造Nginx发送给FastCGI封装的数据给PHP-FPM去进行解析

配置文件路径和加载路径

技术干货 | phpinfo里面有什么?

Configuration File (php.ini) Path指的是PHP默认的配置文件路径,Loaded Configuration File是实际加载的配置文件。这个php.ini文件可有或可无真实的文件(比如docker),其加载的配置文件会表示为"none"。

有无加载php.ini之间,其默认配置会有一些区别。phith0n师傅在小密圈通过Discuz 7.x/6.x的全局变量防御绕过导致代码执行这个例子,提出了这样一个差异,request_order项在apt安装(有php.ini)和docker(无php.ini)下的默认值分别为GP和CGP。

由于这里显示的是apt安装自己设定好的情况,查看安装的模块配置文件是放在Scan this dir for additional .ini files目录,显示加载到的模块详情在Additional .ini files parsed查看,源码安装其值则为none。当源码安装PHP或安装扩展时候,使用设置--with-config-file-path--with-config-file-scan-dir选项来完成,那么在运行时,PHP将扫描加载所有以.ini为后缀的文件,并在显示在Additional .ini files parsed项。假设以上路径可控,即可读可写,那么可控制覆盖PHP配置进行提权等操作。

协议/包装器(Registered PHP Streams)

技术干货 | phpinfo里面有什么?

php://是php特有的协议,用于访问PHP的输入输出流、标准输入输出和错误描述符等功能

  • php://input可访问请求的原始数据的只读流,即POST请求的情况下,php://input可以获取到POST数据,但使用enctype="multipart/form-data"的时候,php://input是无效的
  • php://output只写的数据流,允许允许以printecho一样的方式写入到输出缓冲区
  • php://filter常用到的伪协议,设计用于数据流打开时的筛选过滤应用(即Registered Stream Filters),读文件、读源码等有明显效果

file://访问本地文件系统(绝对路径)guan,比如浏览器打开file:///d:/www/example.html、读取本地文件readfile('file:///etc/passwd');

glob://查找匹配的文件路径模式,查找的文件路径也需要限定在open_basedir范围内

技术干货 | phpinfo里面有什么?

data://可分为三部分,比如data://text/plain;base64,cGhwaW5mbw==

  • 第一部分就是data协议头,标识了这个内容为一个data URI资源
  • 第二部分为MIME类型,表示这个内容以怎样的方式去展现,比如text/plain即以文本类型展示
  • 第三部分为编码设置,默认编码是 charset=US-ASCII,以上示例即为base64

httphttpsftpftps这些再熟悉不过了,略...

zipPHP的读写zip文件的压缩流,zip文件压缩包可以压缩存放一个webshell,利用文件读取或者包含漏洞,比如file=zip:///var/www/html/upload/abc.zip#shell.php可执行利用。除此之外,类似的压缩流协议还有compress.bzip2://compress.zlib://等。

pharPHP的读写归档,在不经过解压的情况下能够被php所访问并且执行。phar配合文件操作直接拓展了PHP反序列化攻击的利用面,并且还能绕过一大部分文件上传检测。

流过滤器(Registered Stream Filters)

技术干货 | phpinfo里面有什么?

PHP在Registered Stream Filters项默认提供了可用过滤器列表,以用于在调用php://filter打开数据流进行读写时,应用相应的流过滤器对数据筛选。

可用过滤器列表如下

  • 压缩过滤器zlibbzip2,与协议相比,压缩过滤器更加通用,能在任何时候压缩任何数据流。例如解压zlib.deflate的数据readfile('php://filter/zlib.inflate/resource=test.deflated');还有zlib.inflate流压缩数据突破libxml解析器限制的实体长度php://filter/zlib.deflate/convert.base64-encode/resource=/etc/passwd
  • 字符串过滤器string.*,字符串转换,string.rot13对字符串rot13编码,string.toupper转换为大写, string.tolower转换为小写,string.strip_tags去除标签
  • 转换过滤器convert.*,CTF中最常用的就是convert.base64-encode读源码读文件php://filter/read=convert.base64-encode/recource=flag.php

核心配置(Core)

技术干货 | phpinfo里面有什么?

allow_url_fopen选项激活了URL形式的fopen封装协议使得可以访问URL对象例如文件。默认的封装协议提供用ftp和http协议来访问远程文件,一些扩展库例如zlib可能会注册更多的封装协议。

allow_url_include允许将具有URL意识的fopen包装器与文件包含函数(include,include_once, require,require_once)一起使用。当该选项与allow_url_fopen同时开启,将实现远程文件包含。

技术干货 | phpinfo里面有什么?

注意这里的两项配置项

  • auto_prepend_file在目前页面执行之前,先require指定文件
  • auto_append_file在执行完目标页面之后,再require指定文件
技术干货 | phpinfo里面有什么?

disable_functions禁用某些内部函数。特别是webshell无法命令执行时,其中一种原因是php.ini使用此指令禁用了能执行系统命令的函数,比如以上的system、exec、shell_exec、passthru、popen等函数。列举几种绕过disable_functions的方法。

  • 尝试被遗漏未禁用的函数,比如pcntl_exec、proc_popen、dl等
  • 利用有漏洞的组件,比如ImageMagick()、bash shellshock(CVE-2014-6271)等
  • 利用环境变量LD_PRELOAD,mail、sendmail、putenv等

此外,AntSword针对该指令开了一个Bypass Disable Functions专题,并提供了相应的插件,有兴趣可以测试一下(https://github.com/AntSwordProject/AntSword-Labs/tree/master/bypassdisablefunctions/)

技术干货 | phpinfo里面有什么?

open_basedir将PHP可访问的活动范围限定在指定的目录,比如open_basedir=/var/www/app1/:/tmp/,那么通过app1访问服务器的用户除了/var/www/html/app1/和/tmp/以外,不可访问其他目录及其文件。但需要注意的是,open_basedir指定的限定实际上是目录名,而不是前缀,例如open_basedir=/var/www/app,那么/var/www/app1和/var/www/app2都是可以访问的,容易造成跨站问题。

cgi-fcgi

cgi是服务器后端编程语言(如php、java)与web服务器(如nginx)之间数据交换的通信协议

fastcgi就是升级版的cgi,本质上跟cgi也是协议。nginx根据fastcgi协议拓展了一个模块,也叫fastcgi,虽然同名但两者有本质区别

php-cgi就是php实现的自带fastcgi的管理器,它本身只能解析请求返回结果,但不会进程管理

php-fpm则是fastcgi协议解析器,用来管理php解释器php-cgi的。php-fpm通过生成新的子进程可以实现php.ini修改后的平滑重启

我们可以这样理解,nginx本不支持对PHP程序的直接调用或者解析,必须间接通过fastcgi调用,假设请求一个php程序,nginx会将请求信息按照fastcgi协议封装成数据包转发给php-fpm,php-fpm拿到以后调度管理php-cgi解析php程序代码

技术干货 | phpinfo里面有什么?

这里是一个Nginx服务器通过php-fpm运行的phpinfo,可以看到的该状态是active

看到cgi.fix_pathinfo=On,你应该会联想到Nginx解析漏洞,但它与php、nginx的版本都无关,属于用户配置不当造成的解析漏洞。PHP获取当前URL请求http://127.0.0.1/index.php/index/index/test的路径信息一般是通过环境变量$_SERVER['PATH_INFO']来获取的,key名PATH_INFO是一个cgi 1.1的标准,cgi.fix_pathinfo选项则与这个value值的获取相关。然而,php先前是将设置PATH_TRANSLATEDSCRIPT_FILENAME,而不是现在的PATH_INFO

默认情况下,cgi.fix_pathinfo是打开的,设置为Off或者为0会导致像以前一样运行。举个例子,以国内最流行的thinkphp框架为例,框架使用PATH_INFO来作为路由载体,该请求是的实际执行脚本SCRIPT_FILENAME是index.php,PATH_INFO为/index/index/test,即访问index模块的index控制器的test方法;当关闭cgi.fix_pathinfo时,访问的则是web根目录下的index.php/index/index/test文件

回到漏洞本身,比如如下的nginx.conf

location ~ .php($|/) {
     fastcgi_pass   127.0.0.1:9000;
     fastcgi_index  index.php;
     set $script    $uri;
     set $path_info "";
     if ($uri ~ "^(.+.php)(/.*)") {
          set  $script     $1;
          set  $path_info  $2;
     }
     include       fastcgi_params;
     fastcgi_param SCRIPT_FILENAME   $document_root$script;
     fastcgi_param SCRIPT_NAME       $script;
     fastcgi_param PATH_INFO         $path_info;
}

php.ini设置(默认开启)

cgi.fix_pathinfo=1

URL请求http://127.0.0.1/evil.jpg/xxx.php将触发以下逻辑

if (script_path_translated &&
    (script_path_translated_len = strlen(script_path_translated)) > 0 &&
    (script_path_translated[script_path_translated_len-1] == '/' ||
    (real_path = tsrm_realpath(script_path_translated, NULL)) == NULL)
   ) {......//略}

以上xxx.php结尾的请求通过了nginx的正则匹配,然后丢给php-fpm,php.ini开启了cgi.fix_pathinfo,将/xxx.php作为PATH_INFO的值,那么evil.jpg就是SCRIPT_FILENAME,php把evil.jpg当作php文件来执行

php从5.3.9开始,php-fpm配置(php-fpm.d/www.conf)新增了一个选项security.limit_extensions来限制值fpm允许解析的脚本扩展名,以预防web服务器配置的错误使用其他扩展名运行php代码

; Limits the extensions of the main script FPM will allow to parse. This can
; prevent configuration mistakes on the web server side. You should only limit
; FPM to .php extensions to prevent malicious users to use other extensions to
; exectute php code.
; Note: set an empty value to allow all extensions.
; Default Value: .php
;security.limit_extensions = .php .php3 .php4 .php5 .php7

会话(session)

技术干货 | phpinfo里面有什么?

php内置了很多中处理器用于存取$_SESSION数据时,会对数据进行序列化和反序列化,Registered serializer handlers配置项可以设置序列化及反序列化时使用的处理器,常用的有以下的三种,对应不同的处理格式

处理器 对应的存储格式
php 键名 + 竖线 | + 经过serialize()函数序列化处理的值
php_binary 键名的长度对应的ASCII字符 + 键名 + 经过serialize()函数序列化处理的值
php_serialize(php>=5.5.4) 经过serialize()函数序列化处理的数组

一般地,默认使用php处理器

技术干货 | phpinfo里面有什么?

引发的安全问题:PHP bug 71101

如果php在反序列存储的$_SESSION数据时使用的处理器和序列化时使用的处理器不同,会导致数据无法正确反序列化,通过特殊的构造,可以伪造任意数据

举个例子,当存储时php_serialize处理,然后调用时php去处理。假设这个时候注入的数据是这样的

<?php
session_start(['serialize_handler' => 'php_serialize',]);
class test{}
$_SESSION['a'] = '|O:4:"test":0:{}';
session_write_close();

那么session中的内容就是a:1:{s:1:"a";s:16:"|O:4:"test":0:{}";},根据反序列化解释,其中a:1:{s:1:"a";s:16:"在经过php解析后被看成了键名,则后面就是构造了一个实例化的test对象注入

技术干货 | phpinfo里面有什么?

php.ini默认关闭session.auto_start,开启与关闭的区别在于时候需要调用函数session_start()开启会话,当值等于On时,执行session_start()将产生新的session_id

技术干货 | phpinfo里面有什么?

session.upload_progress.enabled这个参数是默认开启的,在设置为On的时候,就会在上传的过程中生成上传进度文件,它的存储路径为session.save_path,即基本表现为/var/lib/php/session/sess_{PHPSESSID}

session.upload_progress.cleanup开启时,在清除所有的POST数据后,清除上传进度信息。也就是说,文件上传完毕,这个session会自动被清除

上面提及session.auto_start默认是关闭的,当session.upload_progress.enabled开启,在处理一个POST上传,php会执行$_POST[ini_get("session.upload_progress.name")],成功则启用会话,记录上传进度

技术干货 | phpinfo里面有什么?

session.use_strict_mode可防止会话模块使用未初始化的session ID,即会话模块仅接受由它自己创建的有效的会话 ID, 而拒绝由用户自己提供的会话 ID。php默认是未启用session.use_strict_mode的,这就相当于session文件名可控

session与文件包含

不妨回顾一下HITCON CTF 2018的一道题目One Line PHP Challenge

<?php
 ($_=@$_GET['orange'] && @substr(file($_)[0],0,6)) ==='@<?php' ? include($_) : highlight_file(__FILE__);
curl http://127.0.0.1/session_upload.php -H 'Cookie: PHPSESSID=test' -F 'PHP_SESSION_UPLOAD_PROGRESS=hhh' -F 'file=@/etc/passwd'
ls -a /var/lib/php/session/
# 输出结果sess_test

因为默认开启session.upload_progress.cleanup,在清除session内容之前,其内容大致为session.upload_progress.prefix + hhh(PHP_SESSION_UPLOAD_PROGRESS=hhh) + | + session序列,hhh即为可控的写入内容。因此需要条件竞争,在文件清除之前包含利用技术干货 | phpinfo里面有什么?

当然,回到题目中,还需要结合利用php://filter和base64容错fuzz,以满足包含条件

文件包含phpinfo获取session.save_path的Local Value 先来看看一个登录功能的代码段

if (isset($_POST['username']) && isset($_POST['password'])) {
    $query = $mysqli->prepare('select name, password, uuid from user where name = ? and password = sha1(?)');
    $query->bind_param('ss', $_POST['username'], $_POST['password']);
    $query->execute();
    $query->bind_result($name, $password, $uuid);
    if ($query->fetch()) {
        $_SESSION['user'] = $name;
        $_SESSION['uuid'] = $uuid;
    }

当session记录了用户信息,意味着session内容可控,可通过注册一个包含恶意代码的username传入到session中,然后尝试包含。问题在于,session的文件保存路径未必为默认值,假如同时存在一个phpinfo.php页面,使用当前会话包含该phpinfo页面可获取到session.save_path的Local Value

技术干货 | phpinfo里面有什么?

包含session文件

技术干货 | phpinfo里面有什么?

环境变量

技术干货 | phpinfo里面有什么?

此处主要关注两个变量PHP_ADMIN_VALUEPHP_VALUE

PHP_ADMIN_VALUE可以设定指定的配置项的值,php_value可以设定指定的值,但只能用于PHP_INI_ALLPHP_INI_PERDIR类型的配置项,这两个变量经常出现在攻击php-fpm的场景中,这里的phpinfo环境变量信息是通过PHP-FPM未授权访问漏洞远程代码执行打印出来的

在核心配置(Core)一节中提到auto_prepend_file,在执行目标文件之前先包含指定文件。该漏洞实现代码执行的部分就利用到前面提到SAPI中FPM/FastCGI的安全缺陷,伪造一个fastcgi协议的数据包,这个数据包其中传入的环境变量如下

{
    'GATEWAY_INTERFACE''FastCGI/1.0',
    'REQUEST_METHOD''GET',
    'SCRIPT_FILENAME''/var/www/html/index.php',
    'SCRIPT_NAME''/index.php',
    'QUERY_STRING''?a=1&b=2',
    'REQUEST_URI''/index.php?a=1&b=2',
    'DOCUMENT_ROOT''/var/www/html',
    'SERVER_SOFTWARE''php/fcgiclient',
    'REMOTE_ADDR''127.0.0.1',
    'REMOTE_PORT''12345',
    'SERVER_ADDR''127.0.0.1',
    'SERVER_PORT''80',
    'SERVER_NAME'"localhost",
    'SERVER_PROTOCOL''HTTP/1.1'
    'PHP_VALUE''auto_prepend_file = php://input',
    'PHP_ADMIN_VALUE''allow_url_include = On'
}

即通过PHP_VALUE和PHP_ADMIN_VALUE作为FPM的环境变量“覆盖”了配置信息,auto_prepend_file则设置了在执行目标文件之前,先包含request.body内容,最后将包丢给FPM去执行body中的代码

本地文件包含与phpinfo问题

技术干货 | phpinfo里面有什么?
3

核心配置(Core)中有一个配置项file_uploads默认是开启的,php允许用户向服务器上任意php文件以form-data方式提交请求上传数据

技术干货 | phpinfo里面有什么?

文件被上传后,首先默认地会被储存到服务端的默认临时目录中,可以修改php.ini中的upload_tmp_dir设置为其它的路径。服务端的默认临时目录也可以通过更改PHP运行环境的环境变量TMPDIR来重新设置。一般地,临时目录是/tmp,可以通过函数sys_get_temp_dir()进行查询

然而,即使得知临时目录,也不知道临时文件名。假如同时存在一个phpinfo页面,往phpinfo页面上传文件,通过_FILES变量来获取上传的文件信息,然后在这个临时文件在极短时间被删除的时候,竞争时间包含这个临时文件

技术干货 | phpinfo里面有什么?

<!doctype html>
<html>
<body>
<form action="http://192.168.175.140/info.php" method="post" enctype="multipart/form-data">
    <h3>LFI with phpinfo</h3>
    <label for="file">filename</label>
    <input type="file" name="file">
    <input type="submit" name="submit" value="submit"/>
</body>
</html>
技术干货 | phpinfo里面有什么?
技术干货 | phpinfo里面有什么?

在被php删除临时文件之前,竞争时间包含执行这个tmp_name文件

参考

(https://www.php.net/manual/zh/index.php)

PHP: PHP 8.0.0 Release Announcement (https://www.php.net/releases/8.0/en.php)

AntSword-Labs/bypassdisablefunctions (https://github.com/AntSwordProject/AntSword-Labs/tree/master/bypassdisablefunctions/)

Nginx(PHP/fastcgi)的PATH_INFO问题 (https://www.laruence.com/2009/11/13/1138.html)

PHP :: Bug #71101 :: PHP Session Data Injection Vulnerability (https://bugs.php.net/bug.php?id=71101)

Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写 (https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html)

LFI WITH PHPINFO() ASSISTANCE (https://insomniasec.com/downloads/publications/LFI%20With%20PHPInfo%20Assistance.pdf)

原文始发于微信公众号(安世加):技术干货 | phpinfo里面有什么?

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月12日23:51:37
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   技术干货 | phpinfo里面有什么?http://cn-sec.com/archives/999967.html

发表评论

匿名网友 填写信息