avatar

目录
PHP的rce函数学习

前言

最近复现的漏洞都是以远程代码执行为主。但是基本都是JAVA环境的,对java的也不怎么熟,就只能对PHP的进行学习一下。

代码执行函数

通过搜索引擎了解到了PHP的代码执行函数

1、eval()

定义和用法

eval() 函数把字符串按照 PHP 代码来计算。

该字符串必须是合法的 PHP 代码,且必须以分号结尾。

如果没有在代码字符串中调用 return 语句,则返回 NULL。如果代码中存在解析错误,则 eval() 函数返回 false。

语法

php
1
eval(phpcode)
参数 描述
phpcode 必需。规定要计算的 PHP 代码。

代码

php
1
2
3
4
<?php
$code=$_GET["a"];
eval($code);
?>

利用方式:

代码2

当采用POST请求时,这就相当于PHP一句话了。可以用菜刀连接。

php
1
2
3
4
<?php
$code=$_POST["a"];
eval($code);
?>


2、assert()

assert在PHP中叫断言,是用于在代码中捕捉假设的,可以将断言看作是异常处理的一种高级处理。

assert这个函数在php语言中是用来判断一个表达式是否成立。返回true or false;

断言在程序中的某个特定点该的表达式值为真。如果该表达式为假,就中断操作

assert() 会检查指定的assertion 并在结果为 FALSE 时采取适当的行动(视assert_options而定):point_down:

php
1
2
3
4
5
6
7
8
// 断言操作选项函数
assert_options(ASSERT_ACTIVE, 1); // 默认是打开断言的

# PHP5
bool assert ( mixed $assertion [, string $description ] )

# PHP7
bool assert ( mixed $assertion [, Throwable $exception ] )

assert_options

php
1
2
3
4
5
'ASSERT_ACTIVE=1' // Assert函数的开关
'ASSERT_WARNING =1' // 当表达式为false时,是否要输出警告性的错误提示
'ASSERT_BAIL= 0' // 是否要中止运行;
'ASSERT_QUIET_EVAL= 0' // 是否关闭错误提示,在执行表达式时;
'ASSERT_CALLBACK= (NULL)' // 是否启动回调函数

如果 assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行。跟eval()类似, 不过eval($assertion)只是执行符合php编码规范的$code_str

代码

php
1
2
3
4
<?php
$code=$_GET["a"];
assert($code);
?>

利用方式:

eval的就类似了。


3、preg_replace()

preg_replace 函数执行一个正则表达式的搜索和替换。

语法

php
1
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

搜索 subject 中匹配 pattern 的部分, 以 replacement 进行替换。

参数说明:

  • $pattern: 要搜索的模式,可以是字符串或一个字符串数组。
  • $replacement: 用于替换的字符串或字符串数组。
  • $subject: 要搜索替换的目标字符串或字符串数组。
  • $limit: 可选,对于每个模式用于每个 subject 字符串的最大可替换次数。 默认是-1(无限制)。
  • $count: 可选,为替换执行的次数。

返回值

如果subject是一个数组, preg_replace()返回一个数组, 其他情况下返回一个字符串。

如果匹配被查找到,替换后的 subject 被返回,其他情况下 返回没有改变的 subject。如果发生错误,返回 NULL。

代码:

php
1
2
3
4
5
6
7
#preg_replace('正则规则','替换字符','目标字符')
#执行命令和上传文件参考assert函数(不需要加分号)。
#将目标字符中符合正则规则的字符替换为替换字符,此时如果正则规则中使用/e修饰符,则存在代码执行漏洞
#/e模式(PHP版本<5.5.0)
<?php
preg_replace("/test/e",$_GET["a"],"test");
?>

该函数要利用时,需要使用/e(PHP版本<5.5.0)修饰符使preg_replace()replacement参数当作 PHP 代码

利用方式:

和以往的一样


4、call_user_func(),call_user_func_array()函数

call_user_func()

call_user_func — 把第一个参数作为回调函数调用

语法

php
1
call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] ) : mixed//可以有多个参数,第一个参数为被调用的回调函数,除了第一个参数外,其他参数均为被调用函数的参数

第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。

参数

callback
将被调用的回调函数(callable)。

parameter
0个或以上的参数,被传入回调函数。

Note:

请注意,传入call_user_func()的参数不能为引用传递。

例子

php
1
2
3
4
5
6
7
8
9
10
<?php
function nowamagic($a,$b)
{
echo $a;
echo $b;
}
call_user_func('nowamagic', "111","222");
call_user_func('nowamagic', "333","444");
?>
//显示 111 222 333 444

代码

php
1
2
3
<?php
call_user_func('assert','phpinfo()');
?>

利用方式:

直接打开就会是phpinfo();页面

call_user_func_array()

call_user_func_array()只有两个参数,第一个参数为被调用的回调函数,第二个参数是要被传入回调函数的数组,这个数组必须是索引数组。

语法

php
1
call_user_func_array ( callable $callback , array $param_arr ) : mixed//把第一个参数作为回调函数(callback)调用,把参数数组作(`param_arr`)为回调函数的的参数传入。

参数

callback
被调用的回调函数。

param_arr
要被传入回调函数的数组,这个数组得是索引数组。

例子

php
1
2
3
4
5
6
7
8
9
<?php
function a($b, $c)
{
echo $b;
echo $c;
}
call_user_func_array('a', array("111", "222"));
?>
//显示 111 222

代码

php
1
2
3
<?php
call_user_func_array('assert',array('phpinfo()'));
?>

利用方式:


5、array_map()函数

语法

php
1
array_map(myfunction,array1,array2,array3...)

参数

参数 描述
myfunction 必需。用户自定义函数的名称,或者是 null。
array1 必需。规定数组。
array2 可选。规定数组。
array3 可选。规定数组。

例子

php
1
2
3
4
5
6
7
8
9
10
<?php
function myfunction($v)
{
return($v*$v);
}

$a=array(1,2,3,4,5);
print_r(array_map("myfunction",$a));
?>
//Array ( [0] => 1 [1] => 4 [2] => 9 [3] => 16 [4] => 25 )

代码

php
1
2
3
<?php
array_map('assert',array('phpinfo()'));
?>

利用方式:


6、array_filter()函数

语法

php
1
2
array_filter ( array $array [, callable $callback [, int $flag = 0 ]] ) : array
//依次将 array 数组中的每个值传递到 callback 函数。如果 callback 函数返回 true,则 array 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。

参数

array
要循环的数组

callback
使用的回调函数

如果没有提供 callback 函数, 将删除 array 中所有等值为 FALSE 的条目。更多信息见转换为布尔值。

flag
决定callback接收的参数形式:

  • ARRAY_FILTER_USE_KEY - callback接受键名作为的唯一参数
  • ARRAY_FILTER_USE_BOTH - callback同时接受键名和键值

例子

php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
function even($var)
{
// returns whether the input integer is even
return(!($var & 1));
}
$array = array(6, 7, 8, 9, 10, 11, 12);

echo "Even:\n";
print_r(array_filter($array, "even"));
?>
将输出:
Even:
Array
(
[0] => 6
[2] => 8
[4] => 10
[6] => 12
)

$var &1

php
1
2
3
4
5
(1) 判断int型变量var是奇数还是偶数

var&1 = 0 偶数

var&1 = 1 奇数

代码

php
1
2
3
4
5
6
7
8
#用回调函数过滤数组中的元素:array_filter(数组,函数)
#命令执行func=system&cmd=whoami
<?php
$cmd=$_GET['cmd'];
$array1=array($cmd);
$func =$_GET['func'];
array_filter($array1,$func);
?>

利用方式:

代码注入的防治:

  1. 升级到PHP 7.1,该版本对大部分常见的执行动态代码的方法进行了封堵。
  2. php.ini中,关闭“allow_url_fopen”。在打开它的情况下,可以通过 phar:// 等协议丢给include,让其执行动态代码。
  3. php.ini中,通过disable_functions关闭 exec,passthru,shell_exec,system 等函数,禁止PHP调用外部程序。
  4. 永远不要在代码中使用eval
  5. 设置好上传文件夹的权限,禁止从该文件夹执行代码。
  6. include 文件的时候,注意文件的来源;需要动态include时做好参数过滤。
  7. 对于preg_replace()函数,要放弃使用/e修饰符,也可以使用preg_replace_callback()函数代替。如果一定要使用该函数,请保证第二个参数中,对于正则匹配出的对象用单引号包裹 因为mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit]) /e 修正符使 preg_replace()将 replacement 参数当作 PHP 代码(在适当的逆向引用替换完之后)。

命令执行函数

本质:命令执行产生函数:execshell_execsystempassthru等将参数以dos、shell执行

exec()、system()、popen()、passthru()、proc_open()、pcntl_exec()、shell_exec() 、反引号(`) 实际上是使用shell_exec()函数,此外还要小心mail()函数的安全

system()输出并返回最后一行shell结果。
exec()不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面。
passthru()只调用命令,把命令的运行结果原样地直接输出到标准输出设备上。

popen()、proc_open()不会直接返回执行结果,而是返回一个文件指针

后面再学习一下关于命令执行绕过waf的方式才行。

文章作者: KeyboArd
文章链接: https://www.wrpzkb.cn/rce/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 KeyboArd's Blog
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论