跳转到帖子

游客您好,欢迎来到黑客世界论坛!您可以在这里进行注册。

赤队小组-代号1949(原CHT攻防小组)在这个瞬息万变的网络时代,我们保持初心,创造最好的社区来共同交流网络技术。您可以在论坛获取黑客攻防技巧与知识,您也可以加入我们的Telegram交流群 共同实时探讨交流。论坛禁止各种广告,请注册用户查看我们的使用与隐私策略,谢谢您的配合。小组成员可以获取论坛隐藏内容!

TheHackerWorld官方

记一次多平台免杀PHP木马的制作过程

精选回复

发布于

## 前言

我第一次萌生写一个免杀WebShell的想法是本月初上网的时候。无意间看到阿里安全应急平台线上活动“第三届镇妖恶意代码挑战赛”报名公告。简单看了一下,我发现比赛的内容是用PHP、JSP、Python或者Bash语言编写后门代码。如果编写的代码通过阿里巴巴最新一代检测引擎(即Challenge Range)的检测,且不与其他高手重复,则比赛通过。f7503d6468a2cd1560fb6ec04607475d.png

由于我对PHP语言和网络安全了解很多,所以我报名了PHP Track,打算尝试一下。经过4天的努力,编写了一个215行的PHP命令执行后门,并成功通过了现有数十个云反病毒引擎的检测。但还是很遗憾。当比赛射击场开放时,我上传了后门进行测试。最终我没能通过比赛,心里有些失望(毕竟这是我第一次接触QwQ)。 ┐=͟͟͞͞( ̄ー ̄)┌

于是询问了钉钉群负责人后,我们决定在比赛结束后发布WebShell。算是一次学习经历:54a5cbfbe406375ce9a7270ffa786fca.png

项目地址:1.PerlinPuzzle-Webshell-PHP - Gitee

PerlinPuzzle-Webshell-PHP - GitHub

## 声明

在我于2024 年1 月19 日披露这个WebShell 之前,我从未向任何安全平台提交过绕过漏洞报告,也没有从任何安全平台收到过任何bug 赏金或安全币。

## 绕过情况

文件MD5值:7c0aeaec06454e588120877fd18cd0c8

文件SHA256 值:f53d5dd4de1b39ba5e5751f42f5e97b1a28383cf81c276151ef6066661f4f2b6

最后一次绕过测试于2024年1月18日进行,共使用35个云反病毒引擎进行检测,其中34个成功通过,病毒报告率为2.9%。

成功通过:

- 阿里复魔引擎

- 安恒云沙盒

- 大圣云沙盒(风险评分0.19)

- Hippo WebShell 查杀

- 魔盾云沙盒

- 总共26个微步集成引擎(Microsoft、Kaspersky、IKARUS、Avast、GDATA、Antiy、360、NANO、Rising、Sophos、WebShell、MicroAPT、OneStatic、ESET、小红三、Big Spider、AVG、K7、江民、百度、TrustBook、Panda、ClamAV、百度中国、OneAV、MicroNonPE)

-D盾

- Windows 防御者

- Tinder 安全软件

失败:

- 长汀百川WebShell检测平台

(VirusTotal 的另一张临时截图)5263264c7109a99a8ab73f424fa6b861.png

## 如何使用

要使用此WebShell,您需要向POST方法添加3个参数:

- wpstring    =    ABBCCD

- b ˆ ˆ ˆ ˆ=ˆ ˆ ˆ s

- pcs       =      要执行的命令

例如:

````

POST /perlin.php HTTP/1.1

主机: 127.0.0.1:7000

用户代理: Mozilla/5.0(X11;Linux x86_64;rv:109.0)Gecko/20100101 Firefox/116.0

Accept: 文本/html,应用程序/xhtml+xml,应用程序/xml;q=0.9,图像/avif,图像/webp,*/*;q=0.8

Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2

Accept-Encoding: gzip、deflate、br

Content-Type: 应用程序/x-www-form-urlencoded

连接:关闭

升级不安全请求: 1

Sec-Fetch-Dest: 文档

Sec-Fetch-Mode: 导航

Sec-Fetch-Site: 无

Sec-Fetch-User:1

内容长度: 35

wpstring=ABBCCDb=spcs=hostnamectl

``c9daf6669d39f8dba2b6bc574013a40d.png

## 运行环境

1. 操作系统:Windows、Linux、macOS

2. PHP版本:PHP 5完整版、PHP 7完整版、PHP 8完整版

## 绕过点介绍

- 使用故意编写的变量来覆盖传递参数的漏洞;

- 利用线性代数循环群运算原理制作程序锁;

- 将关键危险字符添加到“Perlin Noise”随机数生成算法生成的数组中;

- 严重危险字符的生成内容由程序锁的运行结果决定。如果操作错误,则无法生成正确的字符;

- 在程序执行期间使用拦截器。如果没有解锁,则阻塞函数返回值传输。

- 用很长的行注释干扰词汇引擎。

## 技术原理说明

该WebShell 由4 个模块组成:

- InazumaPuzzle 程序储物柜类

- PerlinNoise危险函数生成和执行类

- 变量值转移覆盖模块

- 代码执行阻塞模块

下面将对这四个模块进行一一说明。

### 变量值传递覆盖模块

在介绍这个模块之前,我们首先需要了解一下变量覆盖漏洞。

变量覆盖漏洞:可以用自定义参数值替换原始变量值的漏洞。

在PHP实际开发中,我们经常使用以下语句:4fe1aabf0fee0e95346fbaba6eb5eb56.png

相信很多熟悉PHP的读者都知道,这是从HTTP请求注册变量的代码。此代码使用foreach 语句遍历请求数组,然后根据HTTP 参数名称依次创建变量。使用这种方法可以大大简化我们的开发过程,但是如果某些关键变量可以被攻击者控制会发生什么?c56350e21e2dd21fcd7f9badc71aaa7e.png

此时攻击者可以通过GET或POST控制$security_check变量。如果传入一个假值,程序的SQL注入检查就会被关闭,从而导致SQL注入。 (前提是关键变量的赋值定义没有出现在变量注册语句之后)

这个WebShell特意引入了一个变量覆盖语句,并使用该语句传递关键参数:fed8afd5f34e6d100b29e06939e0e3f0.png

上面的代码中,使用foreach语句遍历请求数组来注册变量,然后验证程序解锁的答案的长度,然后将解锁的答案复制到数组中。这样程序锁就可以使用解锁答案。

### 代码执行阻塞模块

该模块由pause()函数组成。该函数接收程序流程的返回值,并检查程序的解锁状态。如果程序解锁,则返回值传递给下面的程序流程。如果没有解锁,则使用die()函数退出程序:21abda2242b8e110086bd552ecf41c4c.png

该模块主要用于针对云扫查引擎进行动态污点分析。

### InazumaPuzzle 程序储物柜

为了对抗云反病毒引擎的动态污点分析,本程序实现了一个线性代数循环群运算模拟器。该模块接收一个PHP数组形式的传入值,然后根据传入值对对象中的四个成员变量进行循环分组操作。当操作完成后,如果四个成员变量的值相等,则认为程序已解锁。

该模块的设计灵感来自于开放世界游戏《原神》中广泛存在于闪电地区的大世界解谜机制阵列“机关方块”。机构阵列由多个小立方体机构组成。当其中一个机构被击中时,它将链接阵列中的其他组件一起旋转。其本质是实现一个线性代数中“循环群”的计算模拟系统。原理图如下:c96d7d8d2ea8287d8dcac94e5d42ab20.png

将其转换为线性代数方程如下:b2ba6fe73e2cf7c83a6faf85c05ebf91.png

作者根据上述内容编写了程序解锁模块类InazumaPuzzle。该类中有4个关键的类成员变量,blockA、blockB、blockC、blockD,用于存储上图中四个机制块的状态;该类还具有以下关键成员函数:

- setBackBlock()

- 命中()

- __AFG50CE4_RG1()

-getLockerStatus()

其中,setBackBlock()函数的作用是当要操作的成员变量的值即将超过循环组$maxType的最大值时,将其重置为循环组$setType的最小值。代码如下:(最大值为2,最小值为0)84536a4996f8d6f1788e0cbc97e396ab.png

可见,如果成员变量的整数值没有达到临界值,则返回false,不对成员变量进行操作;如果达到临界值,则重置并返回true。

根据原理图公式中的数量Aj编写hit()函数的功能,模拟“击中一个方块带动周围方块旋转”的功能:5afc68b34576c795ea53b88f7a30dd2c.png

可以看到这个函数的核心就是调用setBackBlock()函数或者给成员函数值加1。如果setBackBlock()函数返回true,则说明该成员函数值已经使用循环组操作方法加1了,不需要再次加1;否则手动加1。 (例如,如果接收到的传入值为A,则命中A、B;如果传入值为B,则命中A、B、C;以此类推)

__AFG50CE4_RG1()函数的作用是接收答案数组,并根据数组的每个值调用hit()函数。它根据答案值模拟击中盒子。代码如下:0975035a2e1f751a69f00ee2dce73325.png

可以看到,该函数首先加载由变量覆盖传值模块初始化的答案数组变量$puzz_writeup;然后检查数组的长度以及数组中每个答案字符串,如果不符合格式要求则退出程序;然后遍历数组,取出数组中的每个字符串,并作为参数调用hit()函数,实现“hit”功能;最后初始化全局变量$userans,并将其值设置为对象中四个成员变量的总和。

附:根据类构造函数的设计,四个成员变量的初始值分别为2、0、0、2。如果命中正确,四个数字的总和应该是8。

getLockerStatus()函数的功能非常简单。它只是判断对象中的四个成员变量的值是否相等。如果相等,则判定该程序已解锁。代码如下:b16e966b24975bd27b9f93b4c1dc5b59.png

在程序的主干中,程序首先使用变量覆盖值传递模块来传递关键变量,然后初始化InazumaPuzzle类并调用__AFG50CE4_RG1()解锁函数来尝试解锁程序。如果解锁失败,则以下PerlinNoise类无法初始化,程序退出,如图:6729ab6a6c362092225c1c38fa14e909.png

### PerlinNoise 危险函数生成和执行类

柏林噪声:一种随机数生成算法。柏林噪声以随机性为基础,在此基础上利用缓动曲线进行平滑插值,使得最终的噪声效果更加自然。根据采样空间的不同,Perlin噪声可分为一维、二维和三维。这种随机数生成算法常用于计算机图形学中。

通常,一维柏林函数生成器代码如下所示:

````

int[] perm={.};

浮点数[] grad={.};

void Hash(ref int[] 梯度, int x, int y) {

int permIdx[]=new int[2];

permIdx[0]=FindIndex(x);

permIdx[1]=FindIndex(y);

int gradIdx[]=新int[2];

gradIdx[0]=perm[permIdx[0]];

gradIdx[1]=perm[permIdx[1]];

梯度[0]=grad[gradIdx[0]];

梯度[1]=grad[gradIdx[1]];

}

浮动柏林(浮动x){

int x1=地板(x);

int x2=x1 + 1;

浮动grad1=perm[x1 % 255] * 2.0 - 255.0;

浮动grad2=perm[x2 % 255] * 2.0 - 255.0;

浮点数vec1=x - x1;

浮点数vec3=x - x2;

浮点数t=3 * pow(vec1, 2) - 2 * pow(vec1, 3);

浮动产品1=grad1 * vec1;

浮动产品2=grad2 * vec2;

返回产品1 + t * (产品2 - 产品1);

}

````

但作者修改了一维柏林函数生成算法,在生成数组的第400~405位埋入了后门,实现了命令执行函数system()的隐藏调用。

该类中有以下几个关键函数:

-构造函数

- randomFloat() 基于时间的随机值生成器

- __PLvB4CR0_Z() 排列表生成器

- __PLAB4CR0_o() 梯度表生成器

- __CPRBB0R0_l() 带有埋藏后门的柏林噪声发生器

- __HNBB70CA_5()柏林噪声监视器,带有埋藏后门

接下来依次介绍上述功能。

#### 构造函数

构造函数接收4 个参数:

- arrlength Perlin噪声数组长度;

- MAX_INPUT 最大渐变数;

- MIN_INPUT 最小梯度数;

- source 梯度值数据源(如果DIFF_PERLIN开启后门模式)

构造函数首先加载全局程序锁对象,然后调用InazumaPuzzle:getLockerStatus()方法来判断程序锁状态。如果未解锁,则退出程序;然后确定要生成的柏林噪声数的数量;如果不符合要求,就会报错;然后根据source参数判断是否开启后门模式。最后将4个参数配置保存在类成员变量中。22127e7dda54e475aa702e7224b5eb1b.png

#### 基于时间的随机值生成器

randomFloat()函数的原理比较简单,就是根据时间种子和梯度数的极值配置生成每个梯度数。生成的梯度数是小数:732bb0c2cbc29123c1e626aed7d8c658.png

#### 排列表生成器

__PLvB4CR0_Z() 函数是一个排列表生成器。该函数实际上是一个基于时间的随机整数生成器。生成的数字是指定的柏林噪声数组长度,保存在类中的数组成员变量$seeds_array中:3b0489eabf08ffb189642dc18b617cd7.png

#### 梯度表生成器

__PLAB4CR0_o()函数是梯度表生成器,其实际功能比较简单。该函数读取类成员变量$source的值,根据这个值确定梯度数的数据源(实际上只能生成随机值),然后调用randomFloat()函数生成2个随机小数。生成的数字是指定的柏林噪声数组长度。生成的数组存放在类数组成员变量$inputNumArray中:60ba7bc4df3cde46375904d2d841ca14.png

#### 带埋后门的Perlin 噪声发生器

__CPRBB0R0_l()函数是柏林噪声发生器,该函数中嵌入了后门。该函数首先加载全局变量$userans,它的值是lock的四个成员变量的和(一般为8);然后它根据变量$userans的值进行数学运算,指定生成危险功能字母的ASCII值,并将其写入特定位置。 (位400 到405)其他位是正常的Perlin 噪声数。生成的Perlin噪声数组保存在类数组成员变量$perlin_noise中。a55be0affc3793107d04d62ee9b4b589.png

#### 柏林噪声监测仪

__HNBB70CA_5() 函数是柏林噪声显示。该函数使用动态函数调用来执行system()命令来执行该函数。该函数首先使用EL表达式和数字到字符串转换加载三个变量$userans、$b和$pcs(其中$b是危险函数的首字母,$pcs是要执行的命令),然后读取Perlin噪声数组$perlin_noise中危险函数名称区域的值并将其保存到数组$cache_noise中,然后将其值复制到数组$temp_noise中(复制时故意少读一位),然后将其转换为字符串并多次反转、拼接和阻塞。 system()函数在整个程序的第123行执行。319346fc507badf8ec8e2aa6ff42875a.png

### 项目骨干192837b141d790648fbea8c6fcddcc99.png

可以看到,InazumaPuzzle程序锁首先在程序的主干部分创建,然后调用InazumaPuzzle:__AFG50CE4_RG1()函数尝试解锁程序。然后程序创建了一个带有后门的Perlin噪声生成器并开启后门模式,然后生成梯度表和排名表,最后调用内置后门的Perlin噪声打印函数来执行命令。

## 参考文献

- [[数学原理]闪电魔方解密数学原理-原神影响社区-米有社](https://link.juejin.cn/?target=https%3A%2F%2Fwww.miyoushe.com%2Fys%2Farticle%2F17414097)

- [[代码本质]柏林噪音-知乎](https://link.juejin.cn/?target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F206271895%3Fivk_sa%3D1024320u%26utm_id%3D0)

- [可变覆盖漏洞汇总-CSDN博客](https://link.juejin.cn/?target=https%3A%2F%2Fblog.csdn.net%2Fqq_45521281%2Farticle%2Fdetails%2F105849770)

转载自稀土掘金:https://juejin.cn/post/7325495635457130506

作者:御坂No.19008

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。