一般在PHP源码程序都有一个初始安装的功能,如果相关代码没有对参数进行严禁的过滤,可能会导致攻击者访问安装页面(install.php)、或者构造数据包,对网站进行重新安装,从而危害网站安全,甚至直接拿到服务器权限。
安装页面可能产生的漏洞
无验证功能,任意重装覆盖
$_GET['step']跳过限制步骤
变量覆盖导致重装
判断 lock 后跳转无exit
解析 install.php.bak 漏洞
其他特定功能绕过漏洞
步骤2:代码审计
由于是审计安装相关的代码,我们打开install
目录下的install.php
文件,进行分析。
因篇幅问题,本文只截取其中相对重要的代码进行分析。
在其2~5行,代码如下:
if ( file_exists($_SERVER["DOCUMENT_ROOT"].'/sys/install.lock') )
{
header( "Location: ../index.php" );
}
大意为:如果sys目录下存在install.lock
,则跳转到index.php首页,如果不存在,则执行下面的安装程序代码。
因为在网页程序安装成功后,系统会在sys目录下生成install.lock文件,这样就避免了无验证功能,任意覆盖安装
。
但我们也明显发现了一处漏洞,在跳转首页后,没有用到exit
对进程进行结束,就有可能导致重装漏洞。
我们继续往下看,在其116~148行附近,代码如下:
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 );
我们分为三段来看,
第一段:系统执行了创建数据库名的命令。 第二段:系统的配置信息。 第三段:将配置信息写入config.php文件。
因为数据库名称是由我们控制的,可以自定义输入,并且系统将我们输入的数据库名写入到了配置文件:config.php
,而且在我们阅读的过程中,没有看到任何过滤函数。
也就可以尝试在数据库名称后构造语句,尝试插入恶意语句或者其他代码。 在config.php中,就可以直接执行我们插入的语句。
我们现在查看config.php
文件。
<?php
error_reporting(0);
if (!file_exists($_SERVER["DOCUMENT_ROOT"].'/sys/install.lock')){
header("Location: /install/install.php");
exit;
}
include_once($_SERVER["DOCUMENT_ROOT"].'/sys/lib.php');
$host="localhost";
$username="root";
$password="root";
$database="vauditdemo"; --------
$conn = mysql_connect($host,$username,$password);
mysql_query('set names utf8',$conn);
mysql_select_db($database, $conn) or die(mysql_error());
if (!$conn)
{
die('Could not connect: ' . mysql_error());
exit;
}
session_start();
?>
在其第15行是接收数据库名的地方,我们所构造的语句当然也就在这里执行,但是构造的语法一定要符合逻辑与规范,否则网站就会出现报错。
我们可以尝试这样构造:
本来的语句是这样的:
$database="vauditdemo";
我们可以这样构造:
$database="pocabc; -- -"; phpinfo();//";
其中pocabc是要创建数据库的名称,这个可以随意命名,使用-- -
注销掉后面的SQL语句,并使用";
闭合掉原始的内容,最后再使用//
注释掉后面的";
这样即成功将phpinfo()插入到数据库名中,又不影响正常SQL语句的执行。
漏洞修复
在install.php中后添加exit即可,如图:
可以看到,加上exit后,再次提交则显示302转移信息,这样一来就修复了漏洞