发新话题
打印

[翻译]php安全之谜(PHP Undergroud Security)

[翻译]php安全之谜(PHP Undergroud Security)

文章作者:Omnipresent
译文作者:riusksk(泉哥)
信息来源:邪恶八进制信息安全团队(www.eviloctal.com

-[ 作者信息 ]-----------------------------------------------------------------------

标题: "PHP Undergroud Security"
作者: Omnipresent
邮箱: omnipresent@email.it - omni@playhack.net
主页: http://omni.playhack.net - http://www.playhack.net
日期: 2007-04-12

---------------------------------------------------------------------------------



-[ 译者信息 ]-----------------------------------------------------------------------

译者:riusksk(泉哥)

邮箱:riusksk@qq.com

主页: http://riusksk.blogbus.com

日期:2008-11-15

---------------------------------------------------------------------------------



-[ 摘要 ]---------------------------------------------------------------------

     0x00: 前言

     0x01: 关注全局变量
         
  • 修补

         0x02: 文件包含
             
  • 修补

         0x03: 跨站脚本
         0x04: SQL注入

              \_ 0x04a: 绕过登陆验证
              \_ 0x04b: 1 Query? No.. 2 one!(译注:不好翻译,还是保留原文吧!)
             
  • 修补

         0x05: 文件遍历
             
  • 修补
         0x05: 结论


    ---[ 0x00: 前言]

    大家好!首先对我糟糕的英语表示抱歉,因为它不是我的母语.

    在本教程中,我将会向大家介绍一些主要的php漏洞,以及如何发现、利用并修补它!

    -----------------------------------------------------------------------------[/]

    ---[ 0x01: 关注全局变量]


    在php中,你并不一定需要声明变量(这对程序员来说确实是件好事),当你必须使用它时它会“自动”创建的。你可能认为这是件不错的事,确实如此,但这只是偶而发生的事情。

    众所周之,PHP经常要获取用户的输入值,并对其进行处理。假如我们在使用一个未声明过的变量时,就可能会引发web程序的安全问题。例如下面一段代码:

    [...]

         if ($is_admin == 1) {
              //Yes, I'm the admin so call the Administration Pannel
              [...]
         } else {
              //No, I'm not the admin
              [...]
         }



    上面的变量$is_admin ,我们在使用前并未声明过,那么我们是否可以绕过该变量(在bugged.php文件中)而访问未经授权的管理面板呢?答案是肯定,但我们该如何添加管理认证权限呢?其实很简单,比如:

    http://remote_host/bugged.php?is_admin=1


    ---[ 修补 ]---
    如何修补该漏洞呢?这很容易:在if语句前声明变量$is_admin 即可,代码如下:

    $is_admin = 0;

    [...]

         if ($is_admin == 1) {
              //Yes, I'm the admin so call the Administration Pannel(为真,则说明是管理员,因此被重定向管理界面)
              [...]
         } else {
              //No, I'm not the admin(为假,则不是管理员)
              [...]
         }

    -----------------------------------------------------------------------------[/]


    ---[ 0x02: 文件包含]

    -----[本地文件包含]

    PHP是种杰出的语言,强大而简单;但是如果你不想在你的代码中出现安全问题,那么你就必须注意一下你的代码了。

    在一些情况下,使用动态包含文件(部分路径名存在某个变量中)确实是件好事。让我们看一下下面的代码:


    <?php

         include "/users/".$include_path.".php";
         [...]
    ?>



    变量$include_path 在使用前并未进行任何声明,因此攻击者可以在该变量中输入恶意数据,使其包含其它文件(比如/etc/passwd..)。

    用户通过修改URL中的$include_path 变量值可以很容易浏览其它文件。例如:

    http://remote_host/bugged.php?include_path=../../../../etc/passwd%00



    包含后的结果如下:


    <?php

         include "/users/../../../../etc/passwd%00.php"
         [...]
    ?>


    因此攻击者可以窃取到存在服务器上的所有密码。




    - [ 注意 ] -

    %00是一个NULL字符,用于“删除”PHP文件扩展名。如果省略掉NULL,将只会显示PHP文件的内容,因为包含文件的扩展名是PHP(include "/users/".$include_path.".PHP")

    -------------------------------------------------------------------------------



    -----[ 远程文件包含]

    先看一下下面的代码:



    <?php

         [...]

              include($_GET['pag']);

         [...]

    ?>




    正如我们所看到的,变量$page 在使用前并未进行任何验证,因此恶意用户可以通过浏览器包含或者调用他的脚本,从而获得被害者电脑的访问权或者像上面说的一样——浏览文件。

    例1(提权):

    http://remote_host/inc.php?pag=[Evil Script - our shell located on our server]
    例2(浏览文件):

    http://remote_host/inc.php?pag=/etc/passwd


    ---[ 修补 ]---

    如何解决这种问题呢?验证输入值就可以了。用得最多的一个验证方法就是创建一个可访问页面的列表,如下:


    [...]

         $pag = $_GET['pag'];

         $pages = array('index.php', 'alfa.php', 'beta.php', 'gamma.php');
         
              if(in_array($pag, $pages))
              {
                   include($pag);
              {
                   else
                   {
                   die("Hacking Attempt!");
                   }

    [...]

    -----------------------------------------------------------------------------[/]

    ---[ 0x03: 跨站脚本]

    你是否想知道一些关于XSS的资料呢?关于这方面的内容,我的朋友已经写了很多了,我想我没必要再做重复的工作了,具体可以看下面的链接:
         [+] "Cross-Site Scripting for Fun and Profit"
          http://www.playhack.net/view.php?type=1&id=18

         [+] "Applying XSS to Phishing Attacks"
          http://www.playhack.net/view.php?type=1&id=20

    -----------------------------------------------------------------------------[/]

    ---[ 0x03: SQL 注入]

    SQL注入,如名字所述:在一个存在漏洞的WEB程序中,你可以在请求语句中注入SQL语句。
    -----------------------------------------------------------------------------[/]

    ------[ 0x04b 绕过登陆验证]

    在举例之前,我们必须先知晓一些关于SQL的内容:

    - (')  这是什么呢?对攻击者来说,这是一个SQL语法中的操作符,这在漏洞攻击中是非常重要的,它可以用于划分字符串…

    - (#) 用于注释,'#'(没有分号),这在SQL语法中用于作注释用的。记住它,因为它非常实用和重要!

    - (;) 这个符号在数据库中可以用于构造新的请求语句。呆会我们看一下用它来入侵的实际操作,先看一下下面的例子(绕过登陆验证--获取管理权限):


    <?php

         // login.php
             
         $nick = $_POST['nick'];
         $pass = $_POST['pass'];

         $link = mysql_connect('localhost', 'root', 'root') or die('Error: '.
              mysql_error());

         mysql_select_db("sql_inj", $link);


    $query = mysql_query("SELECT * FROM sql_inj WHERE nick ='".$nick."' AND pass ='" .$pass. "'",
    $link);

    if (mysql_num_rows($query) == 0) {
    echo "<script type=\"text/javascript\">window.location.href='index.html';</script>";
    exit;
    }

         $logged = 1;

         [...]

         //EoF

         ?>



    虽然这段脚本代码非常简单,但是这在学习SQL注入时非常有用。正如我们所看到的,变量$nick和 $pass用于SQL请求时并未进行任何有效地验证,因此我们可以注入我们的SQL代码!看一下下面的请求语句:



    "SELECT * FROM sql_inj WHERE nick ='".$nick."' AND pass ='" .$pass. "'"



    What happends if we pass two variables tainted like these:

    假如我们输入经恶意构造的这两个变量,那么将会发生什么事呢,例如:


    $nick = 1' OR '1' = '1
    $pass = 1' OR '1' = '1

    The new query will be:

    构成新的请求语句:

    "SELECT * FROM sql_inj WHERE nick ='1 OR '1' = '1' AND pass ='1' OR '1' = '1'"




    如果你已经知道了关于数据库中的表的知识,但还未弄清楚上面的语句,那么可以看看下面的解释,以帮助你理解它。

    1 OR 1 = 1 ??

    1或者1等于1?很明显的!那么谁是'sql_inj'表中的第一位用户呢?会是安装这一WEB程序的人吗?没错!就是管理员。

    因此我们可以以管理员的身份登陆网站,这样我们就成为管理员了,酷吧!

    我们也可以输入下列请求:


    $nick = 1' OR '1' = '1' #
    $pass = what_we_want_to_put

    构成新一请求语句:
    "SELECT * FROM sql_inj WHERE nick ='1 OR '1' = '1' # AND pass = what_we_want_to_put"



    之前我们已经说过'#'是注释用的!现在我们重新构成新的请求语句:



    "SELECT * FROM sql_inj WHERE nick ='1 OR '1' = '1'



    现在我们又变成管理员了:D


    ------[ 0x04b: 1 Query? No.. 2 one! ]

    通过SQL注入,我们可以通过修改请求语句,从而注入新的数据,更改用户资料,还可以做其它事……让我们看一下下面的源代码:


    <?php

    //email.php

    [...]

    $email = $_POST['email'];

    [...]

    $query = mysql_query("SELECT email, passwd, user_name FROM users WHERE email =
    '".$email."'");
    [...]

    ?>


    在这里,变量$email 在使用前并未进行任何有效地验证,因此我们可以利用它了。比如通过输入像下面的变量值就可以很容易地更新数据库信息了:


    $email = x'; UPDATE users SET email = 'omnipresent@email.it'
          WHERE email = 'admin@site.com ';

    构造新的请求语句:



    SELECT email, passwd, user_name FROM users
         WHERE email = ' x'; UPDATE users SET email = 'omnipresent@email.it'
         WHERE email = 'admin@site.com ';


    这里攻击者可以修改 'users' 表,特别是管理员的邮箱,利用他的邮箱,我可以通过“忘记了密码?”这一链接,利用邮箱(这里是omnipresent@email.it)接收一封像下列内容的邮件,从而达到目的:



    From: host@site.com
         To: omnipresent@email.it
         Subject: Login Password

         Ehy.. take it ;)

         Username: Admin
         Password: 12345

         Regards,
         Admin


    ---[ 修补 ]---
    首先可以通过修改php.ini文件来修补脚本漏洞:

    1. 在所有的' (单引号) , “ (双引号) , \ (反斜线)以及NULL前自动加上转义符——反斜线“\”
        -COOKIE
        -POST
        -GET

    2. 在字符串前加上斜线‘/’

    3. 将指定的字符转换为HTML实体

    4.转义在mysql-query中使用的字符串

    5. 可以到 www.php.net 查看更多的函数

    (译注: www.php.net 是一个不错的PHP在线查询站点,我经常用它来查询PHP函数及其它相关资料。)

    6. 验证用户提取的数据,例如:

        $user_id = (int)$_GET['user_id'];
       
        $user_id is always an integer and we can cast the input for securing
        our web applications.

    变量$user_id 总为整数,这样就可以确保在WEB程序中输入数据的安全了。
    -----------------------------------------------------------------------------[/]

    ---[ 0x05: 文件遍历]

    遍历文件是一个十分危重的漏洞,下面简单讲解一下该漏洞。

    无论在什么时候,当我们需要使用文件时都需要检查一下脚本中的文件名及其路径。

    在大多情况下,文件名都是作为函数fopen()的参数来传输的,比如:

    <?php

    [...]

        $fp = fopen("/path/{$_GET['filename']}.txt", 'r');

    [...]

    ?>


    由于在这段脚本代码中'filename' 可被被远程用户修改破坏,这就造成了文件遍历漏洞。

    在这种情况下,攻击者可以通过使用多个"../"来移动目录查看文件。

    例如:

    http://remote_host/path/bug.php?filename=../../../../path_of_another_file/file%00


    NULL Byte (%00)在许多攻击中多用于截断字符串,以突破文件扩展名的限制。

    ---[ 修补 ]---

    修补这一漏洞最好的方法就是使用basename()函数,例如:


    <?php

        $clean = array();
       
        if (basename($_GET['filename']) == $_GET['filename'])
             {
                  $clean['filename'] = $_GET['filename'];
             }
        else
             {

                  [...]

             }

    $fp = fopen("/path/{$clean['filename']}.txt", 'r');


    ?>


    -----------------------------------------------------------------------------[/]


    ---[ 0x06: 结论 ]

    这是"PHP Undergroud Security"的结尾了,希望本文对你打造php安全代码有所帮助!

    有任何问题可发送邮件至 omnipresent@email.it 或者 omni@playhack.net
    -----------------------------------------------------------------------------[/]

    \=======================[EOF]============== ====== /
  • TOP

    发新话题