长亭百川云 - 文章详情

php代码审计之install.php重装漏洞

41group

68

2024-07-13

欢迎各位大佬入群交流,需要各种资料也可入群领取。

二维码失效加好友进群!!!!!!!!!!!

wx:kalithion

前言:

这套php的源码是之前上课的时候老师发给我的,里面包含了很多的常规的漏洞,个人感觉非常适合初学者来进行审计(本人php菜鸡),今天发的文章也是之前审计过的,个人感觉比较具有实战意义,所以就整理更新到csdn上来,也是为了方便广大的基友们学习。审计步骤:

下面贴出来的是install.php文件的代码:

<?php  
  
if ( file\_exists($\_SERVER\["DOCUMENT\_ROOT"\].'/sys/install.lock') ) {  
    header( "Location: ../index.php" );  
  
}  
  
require\_once '../header.php';  
  
function check\_writeable( $file ) {  
    if ( file\_exists( $file ) ) {  
        if ( is\_dir( $file ) ) {  
            $dir = $file;  
            if ( $fp = @fopen( "$dir/test.txt", 'w' ) ) {  
                @fclose( $fp );  
                @unlink( "$dir/test.txt" );  
                $writeable = 1;  
            }  
            else {  
                $writeable = 0;  
            }  
        }  
        else {  
            if ( $fp = @fopen( $file, 'a+' ) ) {  
                @fclose( $fp );  
                $writeable = 1;  
            }  
            else {  
                $writeable = 0;  
            }  
        }  
    }  
    else {  
        $writeable = 2;  
    }  
    return $writeable;  
}  
  
$sys\_info\['mysql\_ver'\]     = extension\_loaded( 'mysql' ) ? 'OK' : 'NO';  
$sys\_info\['zlib'\]          = function\_exists( 'gzclose' ) ? 'OK' : 'NO';  
$sys\_info\['gd'\]            = extension\_loaded( "gd" ) ? 'OK' : 'NO';  
$sys\_info\['socket'\]        = function\_exists( 'fsockopen' ) ? 'OK' : 'NO';  
$sys\_info\['curl\_init'\]        = function\_exists( 'curl\_init' ) ? 'OK' : 'NO';  
  
echo '<div id="ourhp\_er">';  
echo '<h1>系統環境</h1>';  
echo '<p>服務器操作系統:&nbsp;....................................................................&nbsp;'.PHP\_OS.'</p>';  
echo '<p>Web 服務器:&nbsp;....................................................&nbsp;'.$\_SERVER\['SERVER\_SOFTWARE'\].'</p>';  
echo '<p>PHP 版本:&nbsp;....................................................................&nbsp;'.PHP\_VERSION.'</p>';  
echo '<p>MySQL 版本:&nbsp;....................................................................&nbsp;'.$sys\_info\['mysql\_ver'\].'</p>';  
echo '<p>Zlib 支持:&nbsp;....................................................................&nbsp;'.$sys\_info\['zlib'\].'</p>';  
echo '<p>GD2 支持:&nbsp;....................................................................&nbsp;'.$sys\_info\['gd'\].'</p>';  
echo '<p>Socket 支持:&nbsp;....................................................................&nbsp;'.$sys\_info\['socket'\].'</p>';  
echo '<p>curl 支持:&nbsp;....................................................................&nbsp;'.$sys\_info\['curl\_init'\].'</p>';  
echo '<h1>目錄權限</h1>';  
  
/\* 检查目录 \*/  
$check\_dirs = array (  
    '../sys',  
    '../uploads'  
);  
  
$i = 0;  
foreach ( $check\_dirs as $dir ) {  
    $full\_dir = $dir;  
    $check\_writeable = check\_writeable( $full\_dir );  
    if ( $check\_writeable == '1' ) {  
        echo "<p>".$check\_dirs\[$i\]."&nbsp;...................................................................&nbsp;<font color='#00CC33'>可寫</font></p>";  
    }  
    elseif ( $check\_writeable == '0' ) {  
        echo "<p>".$check\_dirs\[$i\]."&nbsp;...................................................................&nbsp;<font color='#ff0000'>不可寫</font></p>";  
        $no\_write = true;  
    }  
    elseif ( $check\_writeable == '2' ) {  
        echo "<p>".$check\_dirs\[$i\]."&nbsp;...................................................................&nbsp;<b>不存在</b></p>";  
        $no\_write = true;  
    }  
    $i = $i + 1;  
}  
  
if ( $sys\_info\['gd'\] == 'NO' || $sys\_info\['curl\_init'\] == 'NO' ) {  
    exit( '組建不支持,無法安裝使用!' );  
}else if ( $check\_writeable == '0' || $check\_writeable == '2' ) {  
    exit( '關鍵目錄不可寫,無法安裝使用!' );  
}  
  
if ( $\_POST ) {  
  
    if ( $\_POST\["dbhost"\] == "" ) {  
        exit( '数据库连接地址不能为空' );  
    }elseif ( $\_POST\["dbuser"\] == "" ) {  
        exit( '数据库数据库登录名' );  
    }elseif ( $\_POST\["dbname"\] == "" ) {  
        exit( '请先创建数据库名称' );  
    }  
  
    $dbhost = $\_POST\["dbhost"\];  
    $dbuser = $\_POST\["dbuser"\];  
    $dbpass = $\_POST\["dbpass"\];  
    $dbname = $\_POST\["dbname"\];  
  
    $con = mysql\_connect( $dbhost, $dbuser, $dbpass );  
    if ( !$con ) {  
        die( '数据库链接出错,请检查账号密码及地址是否正确: ' . mysql\_error() );  
    }  
  
    $result = mysql\_query('show databases;') or die ( mysql\_error() );;  
    While($row = mysql\_fetch\_assoc($result)){         
        $data\[\] = $row\['Database'\];  
    }  
    unset($result, $row);  
    if (in\_array(strtolower($dbname), $data)){  
        mysql\_close();  
        echo "<script>if(!alert('數據庫已存在')){window.history.back(-1);}</script>";  
        exit();  
    }  
  
    // exp;-- -";phpinfo();//  
  
    mysql\_query( "CREATE DATABASE $dbname", $con ) or die ( mysql\_error() );  
  
    $str\_tmp="<?php\\r\\n";  
    $str\_end="?>";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.="error\_reporting(0);\\r\\n";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.="if (!file\_exists(\\$\_SERVER\[\\"DOCUMENT\_ROOT\\"\].'/sys/install.lock')){\\r\\n\\theader(\\"Location: /install/install.php\\");\\r\\nexit;\\r\\n}\\r\\n";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.="include\_once('../sys/lib.php');\\r\\n";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.="\\$host=\\"$dbhost\\"; \\r\\n";  
    $str\_tmp.="\\$username=\\"$dbuser\\"; \\r\\n";  
    $str\_tmp.="\\$password=\\"$dbpass\\"; \\r\\n";  
    $str\_tmp.="\\$database=\\"$dbname\\"; \\r\\n";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.="\\$conn = mysql\_connect(\\$host,\\$username,\\$password);\\r\\n";  
    $str\_tmp.="mysql\_query('set names utf8',\\$conn);\\r\\n";  
    $str\_tmp.="mysql\_select\_db(\\$database, \\$conn) or die(mysql\_error());\\r\\n";  
    $str\_tmp.="if (!\\$conn)\\r\\n";  
    $str\_tmp.="{\\r\\n";  
    $str\_tmp.="\\tdie('Could not connect: ' . mysql\_error());\\r\\n";  
    $str\_tmp.="\\texit;\\r\\n";  
    $str\_tmp.="}\\r\\n";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.="session\_start();\\r\\n";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.=$str\_end;  
  
    $fp=fopen( "../sys/config.php", "w" );  
    fwrite( $fp, $str\_tmp );  
    fclose( $fp );  
  
    //创建表  
    mysql\_select\_db( $dbname, $con );  
    mysql\_query( "set names 'utf8'", $con );  
    //导入数据库  
    $sql=file\_get\_contents( "install.sql" );  
    $a=explode( ";", $sql );  
    foreach ( $a as $b ) {  
        mysql\_query( $b.";" );  
    }  
    mysql\_close( $con );  
    file\_put\_contents($\_SERVER\["DOCUMENT\_ROOT"\].'/sys/install.lock', 'virink');  
    echo "<script>if(!alert('安裝成功')){window.location.href='../index.php';}</script>";  
    exit;  
}else {  
    echo "<form id='form1' name='form1' method='post' action=''>";  
    echo "<table width='100%' border='0' align='center' cellpadding='10' id='table'>";  
    echo "<tr>";  
    echo "<td colspan='2'><h1></h1></td>";  
    echo "</tr>";  
    echo "<tr>";  
    echo "<td><div align='right'>數據庫连接地址:</div></td>";  
    echo "<td><input name='dbhost' type='text' id='input' value='localhost'/> \*</td>";  
    echo "</tr>";  
    echo "<tr>";  
    echo "<td><div align='right'>數據庫登錄名:</div></td>";  
    echo "<td><input name='dbuser' type='text' id='input' value='root'/> \*</td>";  
    echo "</tr>";  
    echo "<tr>";  
    echo "<td><div align='right'>數據庫登錄密碼:</div></td>";  
    echo "<td><input name='dbpass' type='password' id='input' value='root'/> \*</td>";  
    echo "</tr>";  
    echo "<tr>";  
    echo "<td><div align='right'>創建數據庫名稱:</div></td>";  
    echo "<td><input name='dbname' type='text' id='input' value='vauditdemo'/> \*</td> ";  
    echo "</tr>";  
    echo "<tr>";  
    echo "<td></td>";  
    echo "<td><input type='submit' class='btn' name='Submit' value='安裝' /></td>";  
    echo "</tr>";  
    echo "</table>";  
    echo "</form>";  
}  
?>  
  
  
<?php  
require\_once '../footer.php';  
?>  
  

通过简单的审计得知文件头部判断是否存在当前锁文件/sys/install.lock,如果存在则通过header("Location: ../index.php")返回到主页,但是并没有通过exit()进行退出,所以此处也是漏洞触发的前提条件。代码如下:

  
if ( file\_exists($\_SERVER\["DOCUMENT\_ROOT"\].'/sys/install.lock') ) {  
    header( "Location: ../index.php" );  
  
}

接下来通过代码审计得知自定义了check_writeable()函数来判断目录是否具有读写权限,然后将服务器的一些常用参数列举出来等等,这里就直接忽略了。然后就到了下面一步:

if ( $\_POST ) {  
  
    if ( $\_POST\["dbhost"\] == "" ) {  
        exit( '数据库连接地址不能为空' );  
    }elseif ( $\_POST\["dbuser"\] == "" ) {  
        exit( '数据库数据库登录名' );  
    }elseif ( $\_POST\["dbname"\] == "" ) {  
        exit( '请先创建数据库名称' );  
    }  
  
    $dbhost = $\_POST\["dbhost"\];  
    $dbuser = $\_POST\["dbuser"\];  
    $dbpass = $\_POST\["dbpass"\];  
    $dbname = $\_POST\["dbname"\];  
  
    $con = mysql\_connect( $dbhost, $dbuser, $dbpass );  
    if ( !$con ) {  
        die( '数据库链接出错,请检查账号密码及地址是否正确: ' . mysql\_error() );  
    }  
  
    $result = mysql\_query('show databases;') or die ( mysql\_error() );;  
    While($row = mysql\_fetch\_assoc($result)){         
        $data\[\] = $row\['Database'\];  
    }  
    unset($result, $row);  
    if (in\_array(strtolower($dbname), $data)){  
        mysql\_close();  
        echo "<script>if(!alert('數據庫已存在')){window.history.back(-1);}</script>";  
        exit();  
    }  
  
    mysql\_query( "CREATE DATABASE $dbname", $con ) or die ( mysql\_error() );  

大致的意思就是判断是否存在$_POST提交过来的$dbhost&$dbuser&$dbpass&$dbname等参数,连接数据库并且判断新建的数据库名是否已经存在。接着就是通过mysql_query( "CREATE DATABASE $dbname", $con ) or die ( mysql_error() );来创建数据库名,这需要注意的是(在外部传入的参数$dbname)这里是未做任何过滤就直接带入了sql语句中的。(这里也是漏洞触发的必要条件)接着执行下面的语句:

    $str\_tmp="<?php\\r\\n";  
    $str\_end="?>";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.="error\_reporting(0);\\r\\n";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.="if (!file\_exists(\\$\_SERVER\[\\"DOCUMENT\_ROOT\\"\].'/sys/install.lock')){\\r\\n\\theader(\\"Location: /install/install.php\\");\\r\\nexit;\\r\\n}\\r\\n";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.="include\_once('../sys/lib.php');\\r\\n";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.="\\$host=\\"$dbhost\\"; \\r\\n";  
    $str\_tmp.="\\$username=\\"$dbuser\\"; \\r\\n";  
    $str\_tmp.="\\$password=\\"$dbpass\\"; \\r\\n";  
    $str\_tmp.="\\$database=\\"$dbname\\"; \\r\\n";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.="\\$conn = mysql\_connect(\\$host,\\$username,\\$password);\\r\\n";  
    $str\_tmp.="mysql\_query('set names utf8',\\$conn);\\r\\n";  
    $str\_tmp.="mysql\_select\_db(\\$database, \\$conn) or die(mysql\_error());\\r\\n";  
    $str\_tmp.="if (!\\$conn)\\r\\n";  
    $str\_tmp.="{\\r\\n";  
    $str\_tmp.="\\tdie('Could not connect: ' . mysql\_error());\\r\\n";  
    $str\_tmp.="\\texit;\\r\\n";  
    $str\_tmp.="}\\r\\n";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.="session\_start();\\r\\n";  
    $str\_tmp.="\\r\\n";  
    $str\_tmp.=$str\_end;  
  
    $fp=fopen( "../sys/config.php", "w" );  
    fwrite( $fp, $str\_tmp );  
    fclose( $fp );  

上面代码大致的意思就是将$str_tmp文件内容写入到config.php中,并且$str_tmp中是包含了数据库新建时的信息的,比如$dbname等。刚刚上面也进行了分析,$dbname在外部传入的时候是没有进行任何过滤的,所以我们可以将$dbname的值修改为testva;--";phpinfo()//带入到sql语句中就是mysql_query( "CREATE DATABASE testva;-- ";phpinfo();//", $con ) or die ( mysql_error() );//注释掉后面的语句,--的意思就是在myslq语句中注释后面的其他语句,那么当前语句的意思就是创建一个aaa的数据库,是可以成功执行的。下图是利用成功的案列:

在这里插入图片描述查看源代码config.php 构造的phpinfo()是成功写入的。

在这里插入图片描述随后访问config.php:

修复方案:

在判断是否存在lock文件后,直接加入exit()来结束后面php语句的执行,并且在新建$dbname的时候进行有效过滤即可。

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2