目录

php常见代码命令执行/后门函数

简单记录一些php常用rce/后门/代码命令执行函数

# eval语言构造器

官方定义

1

eval(string $code)

php会执行一个php语句

<?php 
eval($_GET[1]);
?>
1
2
3

提示

php官方认为eval()是语言构造器,不属于函数,编译器将执行的代码视为单独 included 后的文件

官方的说法是:eval语言构造器会把传入的字符串作为一个拼接<?php的php文件包含进来执行

<?php
eval("/*hello */phpinfo();# world!*/");
?>
1
2
3

所以可以等效为下面:

<?php
/*hello */phpinfo();# world!*/
?>
1
2
3

在ctf web方向中,eval执行的还是php语句,不能直接执行linux系统命令例如ls /,cat /flag等,需要使用可以执行系统命令的函数,例如system,passthru,shell_exec等等或者可以用用反引号`包裹系统命令来执行,不会返回执行结果

可以使用echo打印返回结果

<?php
eval("echo `ls`;");
?>
1
2
3

# assert

官方定义

2

assert(mixed $assertion)

最初的作用看起来是当作if判断的,条件为真就往下走,条件为假就抛出异常停止运行。听起来比较鸡肋,不合理的地方出现了,他怎么判断这个字符串是真是假, 他把字符串又传给eval函数执行了一下,看一下返回值,所以assert函数等效eval,后面的参数description经常省略

<?php
assert($_GET[1]);
?>
1
2
3

# system

官方定义

执行外部程序,并且显示输出(执行系统命令并回显)

3

4

system(string $command)

执行 command 参数所指定的系统命令,并且输出执行结果

<?php 
system($_GET[1]);
?>
1
2
3

# passthru

官方定义

执行外部程序并且显示原始输出(执行系统命令并回显)

5

passthru(string $command)

执行系统命令,可以替代 system() 或 exec() 函数

<?php
passthru($_GET[1]);
?>
1
2
3

# exec

提示

exec,shell_exec函数执行命令并返回执行结果,需要主动打印才会回显

官方定义

执行一个外部程序(执行系统命令)

6

exec(string $command, array &$output = null, int &$result_code = null): string|false

常用语法exec(string $command),省略另外两个参数,作为无回显命令执行函数,如果存在第二个参数,会把第二个参数作为数据,回显结果放进数组

7

<?php
exec($_GET[1]);
?>
1
2
3

# shell_exec

官方定义

通过 shell 执行命令并将完整的输出以字符串的方式返回

8

shell_exec(string $command)

执行 command 参数所指定的系统命令,并且返回执行结果

<?php
$msg = shell_exec($_GET[1]);
echo $msg;
?>
1
2
3
4

9

# popen

官方定义

打开进程文件指针

popen(string $command, string $mode)

38

popen函数执行没有返回值,没有回显。适合写文件,反弹shell,外带等

<?php
highlight_file(__FILE__);
popen($_GET[1],'r');
?>
1
2
3
4

类似函数,proc_open(),pcntl_exec()

# include

文件包含模块,php中存在语言结构include,require,以及函数include(),require(),include_once(),require_once()
例如

include"1.png";
require"1.png";
include("1.png");
require("1.png");
1
2
3
4

包含一个任意文件,只要文件内容存在php代码就会被解析执行

详细看:【文件包含】 (opens new window)

# preg_replace()

官方定义

执行一个正则表达式的搜索和替换(在目标串里搜索指定字符串,替换为目标字符串)

10

preg_replace($pattern,$replacement,$subject);

在官方文档里找不到恶意利用的模式了

11

preg_replace函数也可以执行php语句,使用/e模式修饰符,可以把pattern替换到replacement里的php语句进行执行

<?php
preg_replace($_GET[pattern],$_GET[replacement],$_GET[subject]);
?>
1
2
3

/e修饰符已经成为过去式了,php5.5的时候弃用了,但还能用,在php7才彻底移除。换到php5测试一下

12

14

# create_function

官方定义

通过执行代码字符串创建动态函数(使用eval动态生成一个函数)

13

create_function(string $args, string $code)

这个函数会创建一个匿名函数,参数1是匿名函数的参数,参数2是匿名函数的代码

create_function创建匿名函数举个例子

<?php
$a = create_function('$_GET[1]','echo $_GET[1];');
echo $a("capooo");
?>
1
2
3
4

这个过程相当于

<?php
function hello($a){
    echo $a;
}
hello("capooo");
?>
1
2
3
4
5
6

create_function的参数2实际是在eval函数里执行的

这个函数在php8时被官方移除了,低于php8都还能用,试一下

15

创建函数的过程存在一个漏洞,用户可以手动补全}花括号,闭合函数体,执行自己的php语句,最后把多出的}注释掉,或者直接?>结束程序也可以,从而实现漏洞利用

16

# call_user_func

官方定义

17

call_user_func(callable $callback, mixed ...$args)

<?php
highlight_file(__FILE__);
var_dump(is_callable($_GET[1]));
call_user_func($_GET[1],$_GET[2]);
1
2
3
4

call_user_func函数把第一个参数作为回调函数,其他参数作为这个回调函数的参数

回调函数要能够接受参数和处理后面的参数个数,否则会报错

18

提示

使用is_callable()函数判断一个函数是否可调

19

注意eval是语言构造器,不算是函数

20

# call_user_func_array

官方定义

调用回调函数,并把一个数组参数作为回调函数的参数(使用回调函数处理一个数组)

用法和call_user_func类似,区别在于call_user_func的参数是分开的,而call_user_func_array的参数是数组

21

call_user_func_array(callable $callback, array $args)

<?php
highlight_file(__FILE__);
var_dump(is_callable($_GET[1]));
call_user_func_array($_GET[1],$_GET[2]);
1
2
3
4

注意第二个参数是数组,直接传会报错

22

23

# array_map

官方定义

为数组的每个元素应用回调函数(使用回调函数处理每一个数组元素)

24

array_map(?callable $callback, array $array, array ...$arrays)

<?php
highlight_file(__FILE__);
var_dump(is_callable($_GET[1]));
array_map($_GET[1],$_GET[2]);
1
2
3
4

注意第二个参数是数组类型,每个元素都会被回调函数处理

25

# array_filter

官方定义

使用回调函数过滤数组的元素,如果经过回调函数处理,返回true,那么保留这个数组元素,否则过滤掉这个元素

26

array_filter(array $array, callable $callback)

<?php
highlight_file(__FILE__);
var_dump(is_callable($_GET[2]));
array_filter($_GET[1],$_GET[2]);
1
2
3
4

这个函数和上面提到的函数处理有点小变化,他把参数位置交换了

27

# usort

官方定义

使用用户自定义的比较函数对数组中的值进行排序(使用用户调用的函数处理数组)

28

usort(array &$array, callable $callback)

<?php
highlight_file(__FILE__);
var_dump(is_callable($_GET[2]));
usort($_GET[1],$_GET[2]);
1
2
3
4

测试执行了数组第一个元素,第二个元素没有执行,

29

试图找出一些问题,好像找不到

30

# uasort

官方定义

使用用户提供的比较函数对数组中的值进行排序,并保持索引关系

31

uasort(array &$array, callable $callback)

<?php
highlight_file(__FILE__);
var_dump(is_callable($_GET[2]));
uasort($_GET[1],$_GET[2]);
1
2
3
4

命令执行效果和usort函数一样,测试执行了数组第一个元素,第二个元素没有执行

32

# array_walk

官方定义

使用用户自定义函数对数组中的每个元素做回调处理

33

uasort(array &$array, callable $callback)

<?php
highlight_file(__FILE__);
var_dump(is_callable($_GET[2]));
array_walk($_GET[1],$_GET[2]);
1
2
3
4

测试执行了数组第一个元素,第二个元素也执行了,确实挺不错的

34

# array_reduce

官方定义

用回调函数迭代地将数组简化为单一的值

35

array_reduce(array $array, callable $callback, mixed $initial = null): mixed

<?php
highlight_file(__FILE__);
var_dump(is_callable($_GET[2]));
array_reduce($_GET[1],$_GET[2],$_GET[3]);
1
2
3
4

这个函数有3个参数,这回需要用到第三个参数,就不省略了。回调函数把第三个参数作为初始值,在它的基础上再去处理第一个数组参数

可以把第三个参数初始值赋值成恶意命令,在回调函数处理他时,就可以实现命令执行

36

# 动态函数调用

动态调用似乎有玄学问题,感兴趣的师傅自己挖掘一下👀

<?php
highlight_file(__FILE__);
$_GET[1]($_GET[2]);
1
2
3

37

# 反引号

php 反引号执行命令严格来说属于exec函数,如果禁用execshell_exec函数,反引号执行命令的特性不会再生效

<?php
highlight_file(__FILE__);
echo `$_GET[1]`;
1
2
3

# 写文件

通过写文件来实现服务器挂马,例如直接写入php文件,或者说写入.htaccessweb.config文件,实现文件解析漏洞

<?php
highlight_file(__FILE__);
file_put_contents($_GET[1],$_GET[2]);
1
2
3

# 最后

在这些函数中,部分函数在php最新版本已经废弃或者移除,因此复现环境对版本存在依赖,可以使用phpstudy选择php5.5及更早版本复现,

这里提供一套源码,希望对其他正在学习RCE的师傅有所帮助

环境:php5.5

<?php
header('Content-Type: text/html; charset=utf-8');
highlight_file(__FILE__);

if(isset($_POST['1'])){
eval($_POST[1]);
}

$id = isset($_GET['id']) ? $_GET['id'] : 0;
$code = isset($_GET['code']) ? $_GET['code'] : '';

switch ($id) {
   case 0:
     echo 'bye!';
     break;
   case 1:
     eval($code);
     break;
   case 2:
     eval("# $code");
     break;
   case 3:
     assert($code);
     break;
   case 4:
     system($code);
     break;
   case 5:
      passthru($code);
      break;
   case 6:
      exec($code);
      break;
   case 7:
      shell_exec($code);
      break;
   case 8:
      $mode = $_GET[mode];
      popen($code,$mode);
      break;
   case 9:
      include"$code";
      break;
   case 10:
      $pattern = $_GET['pattern'];
      $replace = $_GET['replace'];
      $subject = $_GET['subject'];
      preg_replace($pattern,$replace,$subject);
      break;
   case 11:
      $q = "if it's not fun,why do it?";
      $func = create_function('$q',$code);
      break;
   case 12:
      var_dump(is_callable($_GET['func']));
      call_user_func($_GET['func'],$_GET['args']);
      break;
   case 13:
      var_dump(is_callable($_GET['func']));
      call_user_func_array($_GET['func'],$_GET['array']);
      break;
   case 14:
      var_dump(is_callable($_GET['func']));
      array_map($_GET['func'],$_GET['array']);
      break;
   case 15:
      var_dump(is_callable($_GET['func']));
      array_filter($_GET['func'],$_GET['array']);
      break;
   case 16:
      var_dump(is_callable($_GET['func']));
      usort($_GET['func'],$_GET['array']);
      break;
   case 17:
      var_dump(is_callable($_GET['func']));
      uasort($_GET['func'],$_GET['array']);
      break;
   case 18:
      var_dump(is_callable($_GET['func']));
      array_walk($_GET['func'],$_GET['array']);
      break;
   case 19:
      $_GET['func']($_GET['args']);
      break;
   case 20:
      echo `$code`;
      break;
   case 21:
      file_put_contents($_GET['filename'],$_GET['content']);
      break;
}

echo "Byebye!!"
?>
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

参考、致谢:

最后一次更新于: 2024/11/23, 16:49:24