PHP代码审计——Day7-Bells
漏洞解析
function getUser($id) {
global $config, $db;
if (!is_resource($db)) {
$db = new MySQLi(
$config['dbhost'],
$config['dbuser'],
$config['dbpass'],
$config['dbname']
);
}
$sql = "SELECT username FROM users WHERE id = ?";
$stmt = $db->prepare($sql); // 调用prepare方法来准备SQL语句
$stmt->bind_param('i', $id); // 将一个参数绑定到SQL语句中的占位符,参数值会被正确地转义和处理而非直接插入SQL语句中,有助于防止SQL注入攻击
$stmt->bind_result($name); // 将结果中的第一列的值存储在变量name中
$stmt->execute(); // 执行预处理的语句
$stmt->fetch(); // 从结果集中获取一行数据,并将其存储在已绑定的变量中
return $name;
}
$var = parse_url($_SERVER['HTTP_REFERER']);
parse_str($var['query']);
$currentUser = getUser($id);
echo '<h1>'.htmlspecialchars($currentUser).'</h1>'; // 使用 htmlspecialchars 函数对其进行 HTML 转义,以防止 XSS 攻击
考察点:变量覆盖漏洞
parse_str
导致了变量覆盖漏洞,通过它可以覆盖掉$config
变量,使数据库配置信息可控。
parse_str
:
- 功能 :parse_str的作用是将查询字符串解析到变量中,如果没有array参数,则由该函数设置的变量将覆盖已存在的同名变量。极度不建议在没有 array参数的情况下使用此函数,并且在 PHP 7.2 中将废弃不设置参数的行为。
- 定义 :void parse_str( string $encoded_string [, array &$result ] )
- 使用示例:如果有一个查询字符串 name=John&age=30,使用 parse_str 函数解析后,可以得到两个变量 $name 和 $age,它们的值分别是 'John' 和 '30'。
如果 encoded_string 是 URL 传入的查询字符串(query string),则将它解析为变量并设置到当前作用域(如果提供了 result 则会设置到该数组里 )。
payload:
http://host/?config[dbhost]=10.0.0.5&config[dbuser]=root&config[dbpass]=root&config[dbname]=test&id=1
关于变量覆盖漏洞
漏洞定义:可以用自定义的参数值替换程序原有的变量值,通常需要结合程序的其他功能来实现完整攻击。大多数变量覆盖漏洞是函数使用不当导致的:
extract()
:将数组中的键名作为变量名,键值作为变量值,导入到当前的符号表中(即当前的变量作用域)。这样做的结果是,可以直接使用数组中的键名作为变量在代码中使用。extract(array $array [, int $flags = EXTR_OVERWRITE [, string $prefix = NULL ]]) // $flags:可选参数,用于指定 extract() 函数的行为。默认值为 EXTR_OVERWRITE,表示如果有相同的变量名已经存在,则覆盖它。 // 其他可能的值包括 EXTR_SKIP(跳过已经存在的变量)、EXTR_PREFIX_SAME(如果存在相同的变量名,则添加前缀)、EXTR_PREFIX_ALL(添加前缀到所有变量名)等。 example: <?php $b=2; $a=array('b'=>'123'); extract($a); echo $b; ?> // 输出b为123,但它本来是2
parse_str()
:用于解析查询字符串,并将其中的变量解析到当前的符号表中(即当前的变量作用域),使其成为当前作用域中的变量。它与 $_GET 或 $_POST 等超全局变量一起使用,用于从 URL 查询字符串或 POST 请求中提取数据。void parse_str( string $encoded_string [, array &$result ] ) example: <?php $b=2; parse_str($b=321); print_r($b); ?> // 输出b为321,但它本来是2
import_request_variables()
:它是在没有开启全局变量注册的时候,调用这个函数相当于开启了全局变量注册,在PHP5.4之后,这个函数被取消了。$$
的方式来注册变量,但是没有验证已有变量,导致被覆盖。
$$
的用法:
来看变量覆盖漏洞例子$name = 'name'; $$name = 'Alice'; // 创建了变量$name,其值为'Alice' $name = 'Bob'; // 修改$name的值,不会影响$$name $$name = 'Bobby'; // 这里会创建一个新的变量$Bob,其值为'Bobby' // 如果之前没有$Bob,这相当于覆盖了$$name echo $name; // 输出Bob echo $$name; // 输出Bobby,因为$$name现在指向$Bob
<?php //?name=test $name="thinking"; foreach ($_GET as $key => $value) { $$key = $value; } var_dump($key); var_dump($value); var_dump($$key); echo "</br>".$name; ?>
参考文章
https://lanvnal.com/2020/02/19/rips-php-security-calendar-2017-xue-xi-ji-lu/#toc-heading-2
https://github.com/hongriSec/PHP-Audit-Labs/blob/master/Part1/Day7/files/README.md