返回介绍

4.14 eval 相关的问题

发布于 2024-10-10 22:16:32 字数 9045 浏览 0 评论 0 收藏 0

PHP 和 Perl、Ruby、JavaScript 等多数脚本语言中都提供了将字符串解释为脚本代码并执行的功能。绝大多数情况下,该功能由名为 eval (evaluate 的省略)的函数提供。而本节就将讲述因 eval 的使用方法不当而引起 eval 注入漏洞的问题。

4.14.1 eval 注入

概要

如果 eval 函数的使用方法不当,就有可能导致外界传入的脚本被执行。这被称为 eval 注入 71 攻击,招致此类攻击的漏洞即为 eval 注入漏洞。

71 也有的书中称为“eval 利用攻击”。本书参考了 CWE-95 的命名“Eval Injection”而将其称为 eval 注入。参考: http://cwe.mitre.org/data/definitions/95.html (2010 年 12 月 18 日)。

eval 注入造成的影响与 OS 命令注入攻击相同,具体如下。

  • 信息泄漏
  • 篡改网站
  • 执行非法操作
  • 攻击其他网站(垫脚石)

eval 注入漏洞的对策为实施以下任意一项。

  • 不使用 eval 或与 eval 相当的功能
  • 避免 eval 的参数中包含外界传入的参数
  • eval 的参数中包含外界传入的参数时,将其限定为只包含字母和数字

eval 注入漏洞总览

攻击手段与影响

接下来我们就来看一下 eval 注入的攻击手段及其影响。

  • 存在漏洞的应用

    eval 可以被应用于各种各样的目的,而作为示例,这里我们来看一下将复杂的数据变换为字符串(序列化)后将其在表单之间传递的情况下所产生的漏洞。

    PHP 中存在名为 var_export 的函数,它会将表达式的值以 PHP 代码的形式返回。下面是该函数的执行例。

    <?php
      $e = var_export(array(1, 2, 3), true);  // 将数组转换为 PHP 代码的形式
      echo $e;
    
    

    执行结果

    array (
      0 => 1,
      1 => 2,
      2 => 3,
    )
    
    

    由于执行结果是 PHP 代码的形式,因此能够使用 eval 来回溯得到原先的数据(反序列化)。

    下面为使用 var_export 函数将数组序列化后传递给表单的脚本。

    代码清单 /4e/4e-001.php

    <?php
      $a = array(1, 2, 3);  // 传递的数据
      $ex = var_export($a, true);  // 序列化
      $b64 = base64_encode($ex);   // Base64 编码
    ?>
    <body>
    <form action="4e-002.php" method="GET">
    <input type="hidden" name="data"
      value="<?php echo htmlspecialchars($b64); ?>">
    <input type="submit" value=" 下一步 ">
    </form>
    </body>
    
    

    以上脚本将收到的数据(此处为数组)经过 var_export 函数序列化并使用 Base64 编码加密后将其传递给了 4e-002.php 脚本。

    代码清单 /4e/4e-002.php

    <?php
      $data = $_GET['data'];
      $str = base64_decode($data);
      eval('$a = ' . $str . ';');
    ?>
    <body>
    <?php var_dump($a); ?>
    </body>
    
    

    4e-002.php 将接收到的数据以 Base64 的方式解码,然后使用 eval 还原数据,并通过 var_dum p 函数将其显示在页面上 72 。通过 eval 执行的表达式如下所示。阴影部分为经过 var_export 序列化后的字符串,这里将其赋值给了变量 $a

    $a = array (
    
    
      0 => 1,
    
    
      1 => 2,
    
    
      2 => 3,
    
    
    )
    
    ;
    

    4e-002.php 的执行结果如下图所示,能够看到值被还原了回去。

    图 4-121 示例脚本的执行结果

  • 攻击手段

    4e-002.php 中没有对外界传入的参数进行校验就将其直接传递给了 eval ,因此便存在能够使得外界注入脚本的漏洞。使用下面这种形式,就能够任意添加交由 eval 执行的表达式。

    $a = 表达式 ; 任意语句 ;
    
    
    

    这里我们使用以下注入语句。

    $a = 0; phpinfo()
    
    ;
    

    首先将上面的阴影部分进行 Base64 编码。在 Fiddler 的 Tool 菜单中选 择“Text Encode/Decode”,这时会出现如图 4-122 的对 话框。在上面输入 0; phpinfo(),然后选择左侧的“To Base64”。Base64 编码后的结果会显示在右下方。

    图 4-122 在 Fiddler 中将字符串进行 Base64 编码

    接下来,将编码后的值传给 4e-002.php。URL 和执行结果显示如下。

    http://example.jp/4e/4e-002.php?data=MDsgcGhwaW5mbygp
    
    

    图 4-123 外界注入的脚本被执行了

    由此可以得知,外界注入的 phpinfo 函数被成功执行了。

    而一旦攻击取得成功,PHP 中能够进行的操作就都有可能被用来攻击应用程序。从而就会导致信息被泄漏、数据被篡改、数据库遭到变更、网站被关闭、其他网站受到攻击等各种典型问题。

72 由于 var_dump 函数内部不会进行 HTML 转义,因此这部分存在 XSS 漏洞。

安全隐患的产生原因

evel 能够执行任意的 PHP 脚本代码,可谓是一种极其危险的功能。4e-002.php 中没有校验传给 eval 的参数,因此便使得外界成功地执行了任意脚本。

安全隐患的产生原因能被简单地归纳为如下两点。

  • 使用 eval 本来就是很危险的
  • 没有校验传给 eval 的参数

除了 eval 之外,PHP 中能够解释输入字符串并将其执行的函数还有以下几种。

表 4-23 PHP 中能够解释输入字符串并执行的函数

函数名

解说

create_function()

动态生成函数

preg_replace()

指定 e 修饰符时

mb_ereg_replace()

第 4 个参数指定为 'e' 时

此外,有些函数能够在参数中指定函数名(回调函数),这时如果函数名能够由外界指定,也会产生漏洞。下面列举的例子都属于此类函数。

PHP 中能够在参数中指定函数名的函数

`call_user_func()``call_user_func_array()``array_map()``array_walk()`
`array_filter()``usort()``uksort()` 

对策

防范 eval 注入漏洞的对策如下。

  • 不使用 eval 或与 eval 相当的功能
  • 避免 eval 的参数中包含外界传入的参数
  • 限制外界传入 eval 的参数中只包含字母和数字
  • 不使用 eval

    首先请考虑是否可以不使用 eval 以及与 eval 相当的功能。比如,如果是为了序列化,那么除了 eval 以外,还有以下函数可供选择。

    • implode/explode
    • serialize/unserialize

    implode 函数的参数为数组,通过在各元素之间插入分割字符而将其转换为字符串。 explode 函数的行为则与之相反。这对组合能够胜任简单的序列化处理。

    serialize 的自由度更高,能够序列化对象。但是, unserialize 会生成任意的对象,在对象被销毁时被称为析构函数,有时会成为安全隐患产生的原因 73 。

    而出于序列化之外的其他目的时,也同样应该调查是否有 eval 以外的实现方法。多数情况下,即使不使用 eval 及与其相当的功能,也都是能够实现相同处理的。例如,使用 preg_replace_callback 来取代附带修饰符 epreg_replace ,就能有效提高安全性。

  • 避免 eval 的参数中包含外界传入的参数

    而使用 eval 的情况下,只要外界无法指定其参数就同样无法实施攻击。以 4e-002.php 为例,如果使用会话变量取代 hidden 参数来传递值,外界就无法注入脚本,从而也就保证了安全性。

    然而,脚本的注入途径并不局限于 HTTP 请求,通过文件或数据库等途径也同样有可能注入脚本,因此,如果能够通过这些途径注入,那么就不能使用本对策。

  • 限制外界传入 eval 的参数中只包含字母和数字

    如果能够限制外界传入 eval 的参数中只包含字母和数字,那么就杜绝了脚本注入需要用到的符号字符(如分号 ; 、逗号 , 和引号等),因此也就能够防止脚本注入。

  • 参考:Perl 的 eval 代码块形式

    Perl 语言的 eval 具有两种形式。分别为 eval 后面接表达式的形式,和 eval 后面接代码块(Block)的形式。由于后者能够杜绝 eval 注入攻击,因此便能够放心使用。

    首先我们来看如下脚本,该脚本中使用了 eval 后面跟表达式这一形式,其中含有 eval 注入漏洞。脚本中使用 eval 的目的在于捕捉除以零值时的异常。

    eval("\$c = $a / $b;"); # 除数有可能为零
    
    

    根据以上讲解的内容,如果此处将变量 $b 指定为如下字符串,就会使 /sbin 目录下的文件一览显示出来。

    $b = '1;system("ls /sbin")';
    
    

    而如果像下面的脚本那样采用 eval 的代码块形式,就消除了 eval 注入漏洞。

    代码清单 eval 代码块形式的使用示例(摘要)

    eval {
      $c = $a / $b;   # 除数有可能为零
    };
    if ($@) {  # 出错的情况下
      # 错误处理
    }
    
    

    eval 代码块形式之所以不会产生 eval 注入漏洞,是因为代码块内部的代码是固定不变的。

73 CakePHP 中就曾经被曝出过这样的漏洞。参考: http://cakephp.jp/modules/newbb/viewtopic.php?viewmode=flat&topic_id=2496&forum=3 (日文)

总结

本节介绍了 eval 这类能够将字符串解释为脚本代码并执行的功能中产生的安全隐患。 eval 的功能很强大,但引发漏洞后的影响也同样是非常巨大的。世界上也有很多语言不提供 eval 功能,因此,强烈推荐写代码时不使用 eval

继续深入学习

寺田健的博客文章《通过 preg_replace 执行代码》[2],详细讲述了附带修饰符 epreg_replace 可能产生的漏洞。其中的内容非常有深度,据此还能够学到使用正则表达式来注入脚本等宝贵知识。

GIJOE 所著的《PHP 网络攻击方法》[1] 中也介绍了使用 preg_replace 来进行攻击的例子。其中还提到了因误用 WordPress 中的 call_user_func_array 而导致漏洞的相关内容。

参考文献

写作本节时参考了以下资料。

[1] GIJOE .(2005).《PHP サイバーテロ》(《PHP 网络攻击方法》). ソシム .

[2] 寺田健 .(2008 年 6 月 6 日). preg_replace によるコード実行(通过 preg_replace 执行代码). 参考日期:2010 年 12 月 19 日 , 参考网址:T.Terada の日記 : http://d.hatena.ne.jp/teracc/20080606

[3] 小邨孝明 .(2004 年 10 月 11 日). PHP と Web アプリケーションのセキュリティについてのメモ(关于 PHP 与 Web 应用安全的笔记). 参考日期:2010 年 12 月 19 日,参考网址:個人的なメモと備忘録 : http://www.asahi-net.or.jp/~wv7y-kmr/memo/php_security.html

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文