对 OneNote 传播 STRRAT 恶意样本分析

发布时间 2023-09-05 09:55:15作者: zer0daysec

一、前述

4 月 7 日凌晨 3 点,Unit42 在 Twitter 上发表了一个关于通过 OneNote 进行传播恶意软件的样本推文。从最近国外安全媒体及安全人员发布的信息来看,如今用 OneNote 进行钓鱼的恶意事件逐渐增多,以前也分析过一些 Office 恶意样本,主要是 word 类,但 OneNote 的至今没分析过,看到 Unit42 发的这条推文,兴趣上来了,遂将此样本下下来研究一番探其究竟,这也是我第一次分析 OneNote 恶意样本。

image

二、露出原形

样本信息

image

打开后如下

image

上面提示双击 View Invoice 后再点击 Allow 即可查看后面被模糊的内容,一般像这种的,对于分析过 Office 类的恶意样本老手就再也熟悉不过了,妥妥地为骗局,在 View Invoice 后隐藏了三个 vbe 脚本文件,如下

image

哎哟 ~ 还间隔排在一起 (均匀分布),是生怕受害者点不到是吧,因为这三个文件为同一个文件,文件名为 ..vbe,以 . 为名称会被系统自动隐藏。

三、一层混淆

..vbe 文件中的内容已被混淆,如下

image

下面需要对其进行反混淆,在 110 行处看到 #@~^ 特殊字符 (好像在哪见过),想了一下,记得很早之前 Didier Stevens 写了一个关于对 VBE 的解混淆工具,地址:https://blog.didierstevens.com/?s=decode-vbe, 使用此工具进行解码,如下:

image

工具报错,不过这个工具写的时间比较早,还是 16 年,后面也没再更新,看看它的源码,报错提示不能使用字符串模式应用在字节类对象上,可能是需要 Python2 版本 (后面试了下确实是)

image

但现在基本都是 3 版本,稍微变通一下,在 search 方法中,将 conntent 改为 content.decode() 就可解决,将输出结果重定向到一个新文件中方便查看,如下:

image

导出来的代码还是被混淆了。

三、二层混淆

现在需要对二层混淆的代码进行反混淆,如下

image

iZ 变量的值被 # 符号分隔,每个为数学表达式,计算出每个表达式的值,再将其转为相应的字符,最后使用 eXecuTe 方法来执行解码过后的代码,如下:

image

四、三层混淆

经第二层解混淆后的代码中的 b 变量值被编码了,需要对其进行解码,同样进行调试分析

image

随机生成一个名称为 8 个字符的 js 文件,将解密出来的代码保存在这个 js 文件中,同样也是被混淆了

image

三层混淆的代码任务是执行此 js 文件 (调用 Run 方法执行)。

五、四层混淆

使用 https://github.com/Cqxstevexw/decodeObfuscator 工具对其进行简单处理

image

然后再对此 JS 代码进行调试

image

js 代码会先去判断机器的操作系统及位数再去下载 JRE,下载的 JRE 版本为 1.8,下载目录为用户的临时目录,名称也为随机的 8 个字符

// 随机 8 个字符作为名称
function randomString(_0xfa7bxaa, _0xfa7bxab) {
  _0xfa7bxab = _0xfa7bxab || _0x21b8(330);
  var _0xfa7bxad = _0x24bb[141];

  for (var _0xfa7bxae = 0; _0xfa7bxae < _0xfa7bxaa; _0xfa7bxae++) {
    var _0xfa7bxaf = Math[_0x21b8(374)](Math[_0x24bb[150]]() * _0xfa7bxab[_0x21b8(387)]);

    _0xfa7bxad += _0xfa7bxab[_0x21b8(400)](_0xfa7bxaf, _0xfa7bxaf + 1);
  }

  return _0xfa7bxad;
}

// 获取操作系统类型
function getOS() {
  OsType = readFromRegistry(_0x24bb[156], _0x21b8(369), _0x24bb[93]);

  if (OsType === _0x21b8(359)) {
    return _0x21b8(359);
  } else {
    return _0x21b8(384);
  }
}

// 解压 JRE 文件
function UnZIPJRE(_0xfa7bxbe, _0xfa7bxbf) {
  ExtractTo = _0xfa7bxbf + _0x21b8(409);

  var _0xfa7bxc1 = new ActiveXObject(_0x24bb[117]);

  !_0xfa7bxc1[_0x21b8(370)](ExtractTo) && _0xfa7bxc1[_0x24bb[49]](ExtractTo);

  var _0xfa7bxc2 = new ActiveXObject(_0x21b8(303)),
      _0xfa7bxc3 = _0xfa7bxc2[_0x21b8(349)](_0xfa7bxbe)[_0x21b8(337)]();

  _0xfa7bxc2[_0x21b8(349)](ExtractTo)[_0x24bb[23]](_0xfa7bxc3, 4);

  return ExtractTo;
}

// 解压 JAR 文件
function UnZIPJar(_0xfa7bxc5, _0xfa7bxc6) {
  ExtractTo = _0xfa7bxc6 + _0x21b8(344);

  var _0xfa7bxc8 = new ActiveXObject(_0x21b8(295));

  !_0xfa7bxc8[_0x24bb[66]](ExtractTo) && _0xfa7bxc8[_0x24bb[49]](ExtractTo);

  var _0xfa7bxc9 = new ActiveXObject(_0x24bb[125]),
      _0xfa7bxca = _0xfa7bxc9[_0x21b8(349)](_0xfa7bxc5)[_0x24bb[33]]();

  _0xfa7bxc9[_0x21b8(349)](ExtractTo)[_0x21b8(327)](_0xfa7bxca, 4);

  return ExtractTo;
}

image

最后执行 auz.jar 这个文件

// shell_execute[_0x24bb[73]](JRE, _0x21b8(336) + zip + _0x24bb[140], _0x24bb[141], _0x24bb[141], 0);
shell_execute['ShellExecute'](JRE, 'normalize' + zip + 'auz.jar', '', '', 0);

六、最终魔王

使用 jadx 对其进行反编译,入口 main.Main

image

6.1 Main 类

在 main 方法执行之前首先执行的 static 静态块,如下

image

llIIllIIIl() 对加密的字符串解码

image

解码后的值

image

image

image

解码完成后通过 http://geoplugin.net/json.gp?ip= 接口来获取用户当前所处的位置信息并以返回的 geoplugin_countryCode 字段值命名生成 [geoplugin_countryCode].bat 文件,不过在调试我有个疑惑,如下为判断当前位置处

image

image

光标所在处的代码意思是这个字段中难道会返回多个值?如果有知道的可以在下方留言,但这个肯定与老毛子有关。静态块初始完成后,会去判断给 Main 传的参数是否带有 elevate,如果带有则执行一些操作后最后关机

image

6.2 zonesthesia 类

zonesthesia 类中的 static 块初始化解码字符如下

image

在 Main 类的执行开始处,从中有三处就执行了 rivingly 方法,第一处是获取注册表中 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System 下的 ConsentPromptBehaviorAdmin 项的值,查阅下官方文档 (https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpsb/341747f5-6b5d-4d30-85fc-fa1cc04038d4),5 为默认值,用于在管理员批准模式下提示管理员为需要提升任何非 Windows 二进制文件特权的操作选择允许或拒绝。如果同意管理员选择允许,操作将以最高可用权限继续;第二处是获取 HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System 下的 EnableLUA 项的值,对计算机进行更改时是否将 UAC 通知用户 (UAC 以前称为 LUA);第三处是还是获取 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System 下的 ConsentPromptBehaviorAdmin 项的值。

6.3 unhewn 类

unhewn 类中的 static 块初始化解码字符如下

image

在执行完 zonesthesia 类中的 rivingly() 方法后,就接着执行 unhewn 类中的 rivingly() 方法

image

rivingly() 方法内主要有以下操作:

  • WindowsRegOpenKey(-2147483647,"Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers",983103)
  • WindowsRegSetValueEx(0,"C:\Program Files\Java\jdk1.8.0_361\jre\bin\javaw.exe","~ RUNASADMIN")
  • WindowsRegCloseKey(0)

-2147483647 即 0x80000001,表示 HKEY_CURRENT_USER,983103 即 0xF003F,表示 KEY_ALL_ACCESS,目的是以管理员权限运行 javaw.exe,在 windows 中,在右键点击文件属性弹出框中

image

上面的所有选项都写在注册表中,其中更改所有用户的设置保存在 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers 中,当前用户设置保存在 HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers 中。
注意,以上操作只在给 main 传入参数带有 elevate 时才会执行,当不带有时会去判断当前是否存在 javaw.exe 进程,若存在则直接退出,不存在则执行 cicale 类中方法

image

6.4 cicale 类

cicale 类中的 static 块初始化解码字符如下

image

macarized() 获取电脑名、用户名、处理器标识符和处理器级别,将它们拼接在一起再计算其 MD5 值

image

唉 ~,这 MD5 值不对啊,原因是这样的,这里把它分成了 16 组,每组两位,每两位以十六进制表示,判断其位数是否为 1 位,若是则会以 48 替代,所以这里生成 MD5 值成了 33 位了

image

最后取 MD5 值的前 6 位 1b737c

image

接着去判断当前运行的所在目录是否在 APPDATA (C:\Users\zeronohacker\AppData\Roaming) 下,显然当前不是,所以返回 1 (true),再调用 rivingly() 方法,此方法会再去调用 prepenetrating 类的 rivingly() 方法

image

6.5 prepenetrating 类

prepenetrating 类中的 static 块初始化解码字符如下

image

来到 rivingly() 方法处,传入三个参数,第一个参数为前 33 位 MD5 值的前 6 位,第二个参数为 33 位 MD5 值,第三个参数为 log 字符串

image

判断是否存在 C:\Users\zeronohacker\AppData\Roaming\1b737c 目录,若不存在则创建,并将其设置了隐藏目录,最后生成将 MD5 值的前 6 位、APPDATA 值、33 位值和 log 字符串进行拼接成需要生成的文件

image

其实这个 log 文件就是本身,创建一个名为 JavaConnect 计划任务,每间隔一小时执行在临时目录下的 log 文件

cmd /c schtasks /create /tn "JavaConnect" /tr "C:\Program Files\Java\jdk1.8.0_361\jre\bin\javaw.exe\" -jar \"C:\Users\zeronohacker\AppData\Roaming\1b737c\1b737c84b311b3321ccd5050f2253c48f.log\" /sc minute /mo 60

image

最后返回到 Main 中执行 while 死循环中的 jacklight() 函数,RAT 的基本全部功能都位于 jacklight() 函数中

image

由于 jadx 对其反编译错误,jacklight() 函数代码已乱套,暂且通过手动还原代码来分析。

6.6 核心函数 jacklight()

进入到 jacklight() 函数中,首先创建 biostratigraphy 类,biostratigraphy 类中静态初始化解密相关字符串如下

image

查看 biostratigraphy 类相关方法可得知此类主要负责与网络连接相关,接着调用 macarized() 函数

image

前两个地址目前已无法访问,只有第三个地址现在还存活

image

https[:]//adrenalinecyber.com/login/api.php?action=getIpAddress
https[:]//adrenalinecyber.com/login/api.php?action=getIpAddress
https[:]//www.paradisodomenico.it/wp-content/api.php?action=getIpAddress

获取 IP、电脑名、用户名、MAC 地址、所在国家和当前是否为管理员模式 (当前是读 HKEY_USERS\S-1-5-19)

image

将用户信息上传,至于返回信息是啥暂且不清楚,因为地址已失效

image

不过从访问网址来看,有两个动作,一个是 get_anytask,另一个是 get_tasklist。后面就不再继续添加写了,就另外纯静态分析下。判断返回内容是否包含 kill,其中进入一个 while 循环,这个循环里会将一段 vbs 内容写入到用户临时目录下,文件名为 [country-code].bat,从上面可看出参数带有 elevate,前面对 unhewn 类分析中提到的,可回过头看看。

image

判断是否包含 brower,是否支持浏览,实现远程打开网址

image

判断是否包含 download,实现远程下载

image

判断是否包含 vnc,paralinguistics 类中实现与 vnc 相关操作

image

至此已基本分析结束,此 STRRAT 远控功能比较简单,都是一个 RAT 具备的基本功能,没什么特别的地方。

七、总结

利用 Office 大三件套传播恶意软件在安全界已司空见惯,现今连笔记软件 OneNote 也不放过。虽此次传播只是换了不同以往的载体,但套路还是基本没变,也可能还有新套路,可能是我没见过,或现已出现我还未知,不过还是能期盼遇到 OneNote 高级恶意样本。

八、IOC

HASH

  • 0008069a7d4f9e1539473085c723edd7c647dc7982ae536a3ee7de2bfc24471e
  • 2792f3aa4b0f15c488c32bcd1e03afd8 - auz.jar

URL