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

https://www.cnblogs.com/zzjdbk/p/12985530.html

posted @ 2024-04-09 20:13  smile_2233  阅读(4)  评论(0编辑  收藏  举报