检测恶意包以及它们如何混淆恶意代码

恶意软件包系列4中的第4部分:如何避免和检测恶意软件包,以及它们使用哪些混淆技术来隐藏恶意代码

哇!我们已经看到了恶意软件包系列的最后一篇文章。离别是如此甜蜜的悲伤,我们希望博客一个两个,三个提供恶意包在DevOps和DevSecOps管道中造成严重破坏的见解。

在之前的职位中:

现在让我们了解攻击者在创建恶意包时的其他更谨慎的兴趣:隐藏恶意代码,最后展示如何检测和阻止恶意包。

攻击者用来在恶意包中隐藏有效负载的混淆技术

除了执行成功的感染和有效负载执行外,恶意包作者还希望避免检测到他们的恶意活动。

为了达到合理的攻击成功率,攻击者希望避免被代码分析安全工具检测到,并使安全研究人员难以对其恶意包进行逆向工程。实现这些目标的一种广泛技术是使用代码混淆,这是修改可执行文件的过程,使其对黑客不再有用。然而,它仍然功能齐全。

我们将讨论几种代码混淆技术,包括现成的公共混淆器、自定义混淆技术和不可见的后门技术,后门技术本身不一定是一种混淆方法,而是一种不可见地改变源代码逻辑而不产生可视工件的技术。

公共混淆器示例:python-obfuscator库

2021年7月,JFrog安全研究人员检测到一个名为noblesse的恶意包,它使用了一个公共流行的Python混淆器,简称为Python混淆工具。此工具中使用的混淆机制是使用base64对Python代码进行简单编码,在运行时解码、编译并执行。

进口base64编解码器魔法= ' cHJpbnQ '爱=神“bVxuyoT”=“xvIHdvc”命运= zkxVFVc的快乐= ' \ x72 \ x6f \ x74 \ x31 \ x33 '信任= eval(‘\ x6d \ x61 \ x67 \ x69 \ x63”)+ eval(‘\ x63 \ x6f \ x64 \ x65 \ \ x64 \ x65 \表示就是x63 \ x73 \ x2e x63 \ x6f \ x64 \ x65 \ x28 \ x6c \ x6f \ x76 \ x65 \ x2c \ x20的\ x6a \ x6f \ x79 \ x29”)+ eval(‘\ x67 \ x6f \ x64) + eval(‘\ x63 \ x6f \ x64 \ x65 \ \表示就是x63 \ x73 \ x2e x64 \ x65 \ x63 \ x6f \ x64 \ x65 \ x28 \ x64 \ x65 \ x73 \ x74 \ x69 \ x6e \ x79 \ x2c \ x20的\ x6a \ x6f \ x79 \ x29 ') eval(编译(base64.b64decode (eval(‘\ x74 \ x72 \ x75 \ x73 \ x74 '))<字符串> ','执行'))

在上面的代码片段中,我们可以看到一个示例你好世界打印,这个工具会自动混淆。我们可以看到base64字符串的使用,解码它们b64decode ()函数,以及编译()而且eval ()执行已解码代码的调用。

这种混淆技巧可以欺骗一个简单的静态分析工具,但不能欺骗一个更彻底的分析工具。例如,我们的自动恶意代码检测器可以识别这种简单的混淆技术,并标记被混淆的代码。

控制流扁平化模糊处理技术

控制流扁平化是一种技术,在这种技术中,代码的控制流结构被分解成彼此相邻的块,而不是原始的嵌套级别。

2021年12月,JFfrog安全研究人员发现一个名为discord-lofy的恶意包,它使用了几种混淆技术的组合。其中之一是控制流扁平化。的有效载荷在这个包中有一个Discord令牌抓取器,以及打字和木马感染的方法帮助传播它。

我们可以通过已发表论文中的一个例子来研究这种技术通过控制流扁平化来混淆c++程序下图用一个简单的例子解释了这种方法:

查看图左侧的原始代码;我们可以把它分成三个代码块。首先,我们有变量初始化,然后是一个带有break条件的while循环,最后是while循环中的代码块。

应用模糊处理后,您可以在右侧看到代码。这三个代码块被平铺了,一个开关盒被用来控制代码流。新添加的变量swVar保存执行的代码块的编号。此外,在每个代码块的末尾,它的值会发生变化,以指示应该运行的下一个代码块。

使用同构字符隐藏恶意代码

同构字符方法本身并不是一个混淆器,但它可以用来隐藏合法软件包中的恶意代码修改。这项技术发表在TrojanSource纸并演示了在不可见的情况下更改源代码的可能性。换句话说,代码的逻辑更改以包含漏洞或恶意代码,例如,不会产生任何可视工件。

在这种技术中,攻击者可以使用看起来像普通读者会忽略的标准ASCII拉丁字符的Unicode字符。不过,编译器或解释器会以不同的方式处理它们,因此代码的逻辑会发生变化。

供应链攻击者可以使用这种技术在流行的源代码存储库中植入隐形后门。例如,攻击者可能通过将字符串的一个字符更改为同质符号来更改字符串文字检查或函数调用,使其始终成功或失败。

在下面的代码片段示例中,两个函数看起来是相同的。但是,底部的函数名使用西里尔字母H,这被视为一个完全不同的函数名。程序后面的代码可以以难以区分的方式调用这两个函数中的任何一个。

void sayHello() {std::cout << "Hello, World!\n";}无效说Н嗨(){std:: cout < <“再见,世界! \ n”;}

使用双向控制字符隐藏恶意代码

中介绍的另一种隐形方法TrojanSource纸是Unicode双向(或BiDi)控制字符。这些字符控制文本流(从左到右或从右到左)。在源代码中使用BiDi控制字符时,Unicode编码可能会产生奇怪的工件,例如源代码行以一种方式显示,但编译器以另一种方式解析。

例如,看看下面的原始代码片段:

int main() {bool isAdmin = false;/*只开始管理*/ if (isAdmin) {printf("你是管理员。\n");/* end只管理*/}返回0;}

对于读者来说,代码似乎没有打印“您是管理员”,因为isAdmin = false.但是,如下面的代码片段所示,假设Unicode BiDi控制字符插入到条件检查中的正确位置。在这种情况下,编译器可以将条件检查行解释为完整的注释,这样就可以绕过整个检查。

在条件检查中插入BiDi字符时:

int main() {bool isAdmin = false;' /* begin admin only if (isAdmin) */ {' printf("你是管理员。\n");/* end只管理*/}返回0;}

Anti-Debug技术

除了代码混淆之外,攻击者还通过将调试工具作为恶意代码的一部分检测出来,从而使研究人员和自动化工具的分析过程更加困难。

第一次,JFrog安全研究人员发现并披露一个名为cookiezlog的恶意Python包,它使用了这种反调试技术。在已知的混淆技术中PyArmor和代码压缩时,发现该包使用了一个开源的反调试器Advanced-Anti-Debug

此反调试器调用的函数之一check_processes ()它的目的是通过将活动进程列表与超过50个已知工具的列表进行比较来查看调试器进程是否在系统上运行,包括以下工具:

PROCNAMES = ["ProcessHacker.exe", "httpdebuggerui.exe", "wireshark.exe", " dler.exe", "regedit.exe",…如果proc.name()在PROCNAMES: proc.kill()

如果这些进程中的任何一个正在运行,反调试代码将试图通过终止该进程psutil.Process.kill.阅读我们对反调试器的完整分析最新的博客

现在我们已经了解了恶意包中使用的感染、有效负载、混淆和反调试技术的技术信息,最后让我们讨论在软件开发生命周期(SDLC)中检测恶意包的方法。

如何识别已知和未知的恶意包

检测已知的恶意包

让我们从检测已知的恶意包开始。

为了全面了解项目中的恶意包,我们需要列出项目的依赖项,并检测项目中安装的所有第三方软件版本。此过程的工件称为软件物料清单(SBOM),其中包括关于已安装的第三方软件的信息。

我们可以使用SBOM查询公共存储库,检查我们使用的第三方软件包是否恶意。如果我们以PyPI或npm为例,这些存储库定义了用户可以报告恶意包的进程。要检查已知的恶意包,最有效的方法是查询这些存储库。

不幸的是,在实现这个过程时存在两个问题。第一个问题是许多存储库不保存历史数据。例如,在PyPI中,当恶意包被确认为恶意时,就会从存储库中删除,这样就无法判断过去是否检测到一个包或它的特定版本是恶意的。下面是一个名为ecopwer我们去年披露过。截至今天,在PyPI中搜索这个包时,没有任何证据:

npm中的跟踪稍微好一些。报告的恶意包被替换为伪代码,并被标记为安全防护包,正如您在下面的截图中所看到的,恶意包名为colors-art在npm:

虽然这对于跟踪恶意包很有好处,但对于跟踪合法包的特定恶意版本没有用处,因为当它们被确认为恶意时,所有版本都将从存储库中删除。

第二个问题是,即使我们想使用存储库的数据来扫描恶意包,普通的安全审计工具报告的是漏洞,而不是恶意包。例如,看一下执行PyPI工具的以下结果pip-audit恶意包后ecopower是安装。无法扫描包(如下所示),因为它被报告为恶意后已从PyPI存储库中删除:

由于这两个问题,以及开发人员通常必须执行我们刚刚在规模上描述的审计过程(即,作为SDLC的一部分),我们本质上需要自动化该过程。这可以通过使用集成到我们的开发和CI/CD过程中的软件组合分析(SCA)工具来实现。选择一个安全工具是很重要的JFrog x光它在内部数据库中收集并存储恶意包的名称和版本,而不仅仅依赖于来自npm和PyPI等外部存储库的信息,这些存储库不保存我们所看到的历史数据。

检测未知恶意包

检测未知恶意包在技术上被认为要困难得多,因为我们本质上是处理未知威胁,类似于在漏洞领域中寻找零日。为了检测未知的恶意包,我们需要找到一种方法,在恶意包被认为是恶意之前识别它们的特征。

当我们开发JFrog Xray时,我们采取的方法不仅仅是用最新的已知恶意包名称和版本更新Xray数据库。但是为了检测未知的恶意包,我们还开发并运行启发式扫描仪,扫描公共存储库中的软件包代码,并检测其中的异常。扫描器试图在我们在本博客系列中讨论的任何攻击阶段(感染方法、有效载荷阶段,以及检测隐藏方法或混淆技术)中找到恶意活动的证据。

扫描器对可能的未知恶意包提供警报的能力使它们成为我们发现、研究和披露所有恶意包的基础。在本系列博客中,我们彻底分析了使用这种技术发现的一些恶意软件包我们发布的其他博客

这是我们开发的扫描仪列表。请记住,理论上可以为攻击的每个阶段开发扫描程序,因此尝试将此列表视为启发式技术的演示列表,如果您对查找未知恶意包感兴趣,则可以考虑更多的技术。

我们开发的扫描仪示例:

  • 用于检测依赖关系混淆感染的方法,我们开发了扫描程序,可以在远程公共存储库上找到高版本号的包,并对可能的模仿发出警报。
  • 用于检测下载和执行有效载荷在美国,我们开发了扫描器,使用我们用不同语言监控的系统功能,查找下载二进制文件并执行它的代码模式。
  • 用于检测敏感数据窃取者有效载荷,我们开发了扫描器,使用我们用不同语言监控的系统功能,查找访问文件系统中敏感位置的代码模式。
  • 为了检测混淆技术,我们开发了对base64解码和公共混淆器的其他代码特征发出警报的扫描器。

避免恶意包的安全开发的最佳实践

我们已经接近这个博客系列的结尾了,但是在结束之前,我们将为您提供几个处理恶意包安全威胁的安全开发最佳实践:

  • 处理恶意包威胁最重要和最基本的方法是使用软件组合分析工具作为SDLC的一部分,如JFrog Xray或其他软件组合分析工具。
  • 定义策略并将操作自动化作为DevSecOps流程的一部分。如果在过程中发现恶意包,建议采用中断构建过程并警告问题的策略。
  • 为了防止依赖混淆感染方法,我们希望避免自动获取高版本恶意包,除非我们在已发布软件的新版本上执行DevOps和DevSecOps测试。要实现这一点,建议将构建系统配置为排除远程存储库对于内部包和使用严格的版本对于每个构建的外部依赖。
  • 使用开源工具来帮助检测恶意包,并防止它们感染您的项目:
    • Jfrog-npm-tools: JFrog开发并向社区发布的用于npm包安全的开源工具。
    • piproxy: JFrog为pip开发的一个小型代理服务器,它只在任何内部存储库中都没有找到包时才修改pip的行为来安装外部包。这修复了pip中的依赖项混淆问题。
    • npm_domain_check: JFrog开发的一个工具,用于检测可能被劫持的npm依赖项域的收购
    • 困惑:该工具用于检查多个包管理系统中的依赖项混淆漏洞。
    • PyPI-scan:此工具检查名称的相似性以查找排版包。
  • Pyrsia。io: JFrog去年宣布的一项新的开源计划,用于创建一个安全的、分布式的、为软件包提供完整性的包存储库。该项目使用区块链技术建立开源包的来源链。在这里阅读更多信息pyrsia.io

这篇文章是我们恶意软件包博客系列的结束语,但这并不是告别。

注册JFrog 's即将到来的网络研讨会继续你的教育。