4.14 eval 相关的问题
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 中能够解释输入字符串并执行的函数
函数名 | 解说 |
---|---|
| 动态生成函数 |
| 指定 e 修饰符时 |
| 第 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
来取代附带修饰符e
的preg_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],详细讲述了附带修饰符 e
的 preg_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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论