一段时间以来, ESET 的研究人员一直在跟踪 Winnti 的活动, 该组织从 2012 年起就开始活跃, 并针对视频游戏和软件行业供应链进行攻击. 最近, 发现了一个以前未经记录的后门, 其目标是 Microsoft SQL(MSSQL). 这个后门与 PortReuse 后门有多处相似之处, PortReuse 是 Winnti Group 使用的另一个工具, 于 2019 年 10 月首次记录.
今年检测到了一个新后门的样本, skip-2.0, 作者是 winnti 组织成员. 这个后门程序以 MSSQL 服务器 11 和 12 为目标, 攻击者可以使用 magic 密码连接到任何 MSSQL 帐户, 同时自动将这些连接隐藏在日志中. 后门允许攻击者复制, 修改或删除数据库内容, 可以用来操纵游戏中的货币以获取经济利益. 据了解, skip-2.0 是第一个公开记录的 mssql 服务器后门.
本文将重点介绍 mssql 服务器后门的技术细节和功能, 以及 skip.2-0 与 winnti 已知武器库 (特别是 portreuse 后门和 shadowpad) 的技术相似性.
vmprotected 启动程序
我们在查找 vmprotected 启动程序时找到了 skip-2.0, 其有效负载通常是 portreuse 或 shadowpad.
嵌入式有效载荷
与加密的 portreuse 和 shadowpad 有效负载一样, skip-2.0 嵌入到 vmprotected 启动程序中, 如图 1 所示:
加密
有效负载加密与其他 vmprotected 启动程序中使用的相同. 它是 RC5 加密的, 密钥来自 volumeID 和字符串 f@ukd!RCTO R$.
持久性
与 portreuse 和 shadowpad 的一样, 启动程序可能会通过利用 dll 劫持而持续存在, 方法是将其安装在 c:\Windows\system32\tsvipsrv.dll. 这将导致标准 Windows SessionEnv 服务在系统启动时加载 DLL.
打包器
一旦解密, 嵌入的有效负载实际上是 winnti group 的自定义打包程序. 这个打包器与我们在 白皮书 中记录的代码是相同的. 它被用来打包 portreuse 后门以及嵌入在受损视频游戏中的负载.
配置
打包程序配置包含打包二进制文件的解密密钥及其原始文件名, 大小和执行类型(exe 或 dll). 有效载荷配置如表 1 所示.
打包器配置可以看出, 有效负载称为内部装载器. 内部加载程序是一个注入器的名称, 它是 winnti 集团的武库的一部分, 用于将 portreuse 后门注入监听特定端口的进程.
内部加载器
这是一种内部加载程序的变体, 不是像注入 portreuse 后门时那样寻找监听特定端口的进程, 而是寻找名为 sqlserv.exe 的进程, 这是 mssql server 的常规进程名. 如果找到, 则内部加载程序会将有效负载注入此进程. 此有效负载还与自定义打包程序打包在一起, 该有效负载的打包程序配置如表 2 所示.
此注入负载的原始文件名为 skip-2.0.dll.
skip-2.0
在被内部加载程序注入并启动之后, skip-2.0 首先检查它是否在 sqlserv.exe 进程中执行, 如果是, 则检索 sqllang.dll 的句柄, 该句柄由 sqlserv.exe 加载. 然后继续从该 dll 中查找并挂接多个函数. 图 2 描述了 skip-2.0 的运行过程.
Hooking sqllang.dll
skip-2.0 使用的 hook 过程与 netagent 非常相似, netagent 是负责安装网络 hook 的 portreuse 模块. 这个 hook 库基于 distorm 开源反汇编程序, 该反汇编程序由多个开源挂接框架使用. 需要一个反汇编库来正确计算要 hook 的指令的大小. 在下图中可以看到, NetAgent 和 Skip-2.0 使用的 hook 过程几乎相同.
图 3Hex-Rays output comparison between the NetAgent (left) and skip-2.0 (right) hooking procedures
有一个显著的区别就是 skip-2.0 中的 hooking 函数将要安装的钩子的地址作为参数, 而对于 netagent, 要安装的钩子的地址是硬编码的. 这是因为 skip-2.0 必须 hooksqllang.dll 中的多个函数才能正常运行, 而 netagent 只针对一个函数.
要定位 hook 的每个 sqllang.dll 函数, skip-2.0 首先通过解析 pe 头来检索加载到内存中的 dll 的大小(即其虚拟大小). 然后初始化 sqllang.dll 中要匹配的字节数组, 如图 4 所示. 一旦找到与字节数组匹配的第一个匹配项的地址, 就会使用图 3 所示的过程安装钩子.
然后, 钩子安装成功后会在 cleartext 中记录, 该文件位于硬编码路径 c:\ Windows\temp\ts\u 2ce1.tmp 中, 如图 5 所示.
如果找不到目标函数, 钩子安装程序将搜索具有不同字节模式集的回退函数.
通过匹配字节序列来定位目标函数的地址, 而不是使用静态偏移量, 再加上使用字节的回退序列, skip-2.0 可以更灵活地适应 mssql 更新, 并可针对多个 sqllang.dll 更新.
密码控制
skip-2.0 的目标函数与身份验证和事件日志记录相关. 目标功能包括:
- CPwdPolicyManager::ValidatePwdForLogin
- CSECAuthenticate::AuthenticateLoginIdentity
- ReportLoginSuccess
- IssueLoginSuccessReport
- FExecuteLogonTriggers
- XeSqlPkg::sql_statement_completed::Publish
- XeSqlPkg::sql_batch_completed::Publish
- SecAuditPkg::audit_event::Publish
- XeSqlPkg::login::Publish
- XeSqlPkg::ual_instrument_called::Publish
其中最有趣的函数是第一个函数(cpwdpolicymanager::validatepwdforlogin), 它负责验证为给定用户提供的密码.
此函数的钩子检查用户提供的密码是否与 magic 密码匹配; 如果是, 则不会调用原始函数, 钩子将返回 0, 从而允许连接. 然后设置一个全局标志, 该标志将由负责事件日志记录的其他 hook 函数进行检查. 相应的反编译过程如图 6 所示. 在设置此全局标志的情况下, hook 的日志记录函数将静默返回, 而不调用其对应的原始函数, 因此不会记录操作.
如果使用 magic 密码登录, reportloginsaccess 和 issueloginsuccessreport 挂钩将不会调用原始函数. 同样的行为也适用于 feexecutelogontriggers. 其他日志记录功能, 如 xesqlpkg::sql_completed::publish 或 xesqlpkg::sql_batch_completed::publish, 在用户使用魔法密码登录的情况下也将被禁用. 还禁用了多个审核事件, 包括 secauditpkg::audit_event::publish,xesqlpkg::login::publish 和 xesqlpkg::uau instrument_called::publish.
这一系列 hook 不仅允许攻击者通过特殊密码在受害者的 mssql 服务器中获得持久控制, 而且使用该密码时禁用了多个日志, 因此无法检测到攻击者.
研究人员对多个 MSSQL Server 版本测试了 Skip-2.0, 发现能够使用 MSSQL Server 11 和 12 的密码成功登录. 为了检查 skip-2.0 是否针对特定的 sqllang.dll 版本, 创建了一个 yara 规则, 该规则可以在 库中找到.
与 Winnti 的联系
skip-2.0 和来自 winnti 的其他工具有很多相似之处. vmprotected 启动程序, 自定义打包程序, 内部加载程序和 hook 框架是 winnti 工具集的一部分.
总结
skip-2.0 后门是 winnti 的工具集之一, 它与该组织已知的工具集有很多相似之处, 并允许攻击者在 mssql 服务器上实现持久控制. 安装钩子需要管理权限, 所以必须在已经被攻陷的 mssql 服务器上使用 skip-2.0 来实现持久控制和隐蔽.
来源: http://www.tuicool.com/articles/IRbQRfI