xz liblzma 供应链CVE-2024-3094分析

admin 2024年4月3日18:40:37评论20 views字数 7172阅读23分54秒阅读模式







背景








3月29日,开发人员Andres Freund在oss-security上发布上游 xz/liblzma 中的后门导致 ssh 服务器受到攻击的发现,这是一套为开发人员提供无损压缩的软件。该软件包通常用于压缩发行版 tarball、软件包、内核映像和 initramfs 映像。该项目遭到供应链攻击,项目维护者jiaT75(jia Tan)通过上传二进制测试文件和篡改编译脚本,使得编译过程中恶意二进制文件会替换原有文件,导致编译输出与公开的源码不匹配。

xz liblzma 供应链CVE-2024-3094分析

jia Tan的攻击者,在整个项目中的攻击时序如下图所示:

xz liblzma 供应链CVE-2024-3094分析








影响版本








xz/liblzma:v5.6.0,v5.6.1
xz 5.6.0 和 5.6.1 还没有被广泛整合到 Linux 发行版中,但是依旧建议使用检测脚本检查是否满足条件。







检测脚本








修改自Andres公开邮件原文:
  • 目标系统:只针对 x86-64 架构的 Linux 系统
  • 构建环境:需要使用 gcc 和 GNU 链接器进行构建
#!/bin/bash
if ! (echo "$build" | grep -Eq "^x86_64" > /dev/null 2>&1) && (echo "$build" | grep -Eq "linux-gnu$" > /dev/null 2>&1);then
    echo "System not x86-64 linux. Exiting."
    exit 1
fi

# Building with gcc and the gnu linker
if test "x$GCC" != 'xyes' > /dev/null 2>&1;then
    echo "GCC not found. Exiting."
    exit 1
fi
if test "x$CC" != 'xgcc' > /dev/null 2>&1;then
    echo "CC not set to gcc. Exiting."
    exit 1
fi
LDv=$LD" -v"
if ! $LDv 2>&1 | grep -qs 'GNU ld' > /dev/null 2>&1;then
    echo "GNU ld not found. Exiting."
    exit 1
fi

# Running as part of a debian or RPM package build:
if test -f "$srcdir/debian/rules" || test "x$RPM_ARCH" = "xx86_64";then
    echo "Running as part of a debian or RPM package build."
else
    echo "Not running as part of a debian or RPM package build. Exiting."
    exit 1
fi
# Injected code likely to work only on glibc based systems
echo "Injected code likely to work only on glibc based systems."
# Mention about xz versions
echo "Mention about xz 5.6.0 and 5.6.1 not widely integrated yet."
# If everything passes, the system is likely affected
echo "System appears to be affected by the vulnerability."
# Add further actions here if system is affected
exit 0







组件分析














Stage 0 编译阶段








以下是 m4/build-to-host.m4中的编译代码:

xz liblzma 供应链CVE-2024-3094分析

其作用是从文件中读取来自tests/files/bad-3-corrupt_lzma2.xz的字节,并将其输出到下一步的标准输出/输入,读取所有内容后,还会添加换行符 (n)。然后运行 tr (translate,如“将字符映射到其他字符”或“将字符替换为目标字符”),这基本上将选定的字符(或字节值)更改为其他字符(其他字节值)。 tr "t -" " t-",它对从tests/files/bad-3-corrupt_lzma2.xz 文件流式传输的字节进行以下替换:

  • 0x09 (t) 替换为 0x20
  • 0x20(空格)被替换为 0x09
  • 0x2d (-) 替换为 0x5f
  • 0x5f (_) 替换为 0x2d

实际上是“修复”了bad-3-corrupt_lzma2.xz,使其再次形成一个正确的xz流。







Stage 1 使用bash提取恶意代码








通过bash命令解压缩和执行代码的脚本:

xz liblzma 供应链CVE-2024-3094分析

脚本执行步骤如下:

  • 检查系统是否是 Linux,如果不是则退出脚本。

  • 尝试从 config.status 文件中获取 srcdir 变量的值,并在此基础上设置 srcdir 变量。

  • 定义了一个非常长的命令 i,这个命令包括了很多次的 head 和 tail 操作,可能是在对数据进行裁剪和处理。

  • 通过管道将一个压缩文件 good-large_compressed.lzma(位于 $srcdir/tests/files/ 目录下)解压缩,并将输出通过 tr 命令进行转换,然后交给 /bin/sh 命令执行。







Stage 2 篡改编译代码








该bash文件提取自公开邮件infected.txt,部分脚本内容截图:https://www.openwall.com/lists/oss-security/2024/03/29/4/1

xz liblzma 供应链CVE-2024-3094分析

该部分脚本在所调查的 TAR 存档(5.6.0 和 5.6.1)中都不存在任何带有任何签名的文件。整个功能基本上看起来像一个“扩展/修补”系统,允许添加未来的脚本以在第 2 阶段的上下文中运行,而无需修改原始的负载测试文件。

功能过程:
1.  尝试在tests/files/目录中查找(grep -broaF)两个文件,其中包含以下字节(签名):
Fragment 1: "~!:_ W" and "|_!{ -"
Fragment 3: "jV!.^%" and "%.R.1Z"

2.  如果找到这样的文件,则提取每个文件的偏移量(cut -d: -f2,假设 : 是字段分隔符,则取第二个字段),第一个偏移量 + 7 保存为 $start,第二个偏移量保存为 $start第二个文件中的内容保存为 $end。

3.  一旦脚本有了 $start 和 $end 偏移量,它就会切出文件中具有第一个签名的部分。

4.  接下来首先是替换密码

tr "5-51204-37752-115132-203-4116-131" "-377"

5.  解压数据:

eval ... | xz -F raw --lzma2 -dc

压脚本参考附件1。








后门分析








后门结构体保存:

xz liblzma 供应链CVE-2024-3094分析

后门参数初始化:

xz liblzma 供应链CVE-2024-3094分析

后门加密函数位置:

xz liblzma 供应链CVE-2024-3094分析

函数加入了反调试检查,如_Llzma_index_iter_rewind_cold在安全模式中运行函数,检查返回地址:

xz liblzma 供应链CVE-2024-3094分析

_Llzma_delta_decoder_init_part_0中建立虚表,指向后门恶意功能:

xz liblzma 供应链CVE-2024-3094分析

一些ELF以及环境检查,比如位于Llzma_simple_props_size_part_0检查GUN信息:

xz liblzma 供应链CVE-2024-3094分析

后门包含很多可疑hook函数,同时包含一个伪造的分配器对象,该对象查找符号而不是分配,并且在释放时不执行任何操作。位于Linit_pric_table_part_1中:

xz liblzma 供应链CVE-2024-3094分析

核心功能位于_Llzma_delta_props_encode_part_0Llzma_index_stream_flags_0中,包括寻找指定库函数:

xz liblzma 供应链CVE-2024-3094分析

后门初始化位于0xA784处:

xz liblzma 供应链CVE-2024-3094分析

反编译代码示例:

__int64 backdoor_init(rootkit_ctx *ctx, DWORD *prev_got_ptr)
{
  _DWORD *v2;
  __int64 runtime_offset;
  bool is_cpuid_got_zero;
  void *cpuid_got_ptr;
  __int64 got_value;
  _QWORD *cpuid_got_ptr_1;

  ctx->self = ctx;
  // store data before overwrite
  backdoor_ctx_save(ctx);
  ctx->prev_got_ptr = ctx->got_ptr;
  runtime_offset = ctx->head - ctx->self;
  ctx->runtime_offset = runtime_offset;
  is_cpuid_got_zero = (char *)*(&Llzma_block_buffer_decode_0 + 1) + runtime_offset == 0LL;
  cpuid_got_ptr = (char *)*(&Llzma_block_buffer_decode_0 + 1) + runtime_offset;
  ctx->got_ptr = cpuid_got_ptr;
  if ( !is_cpuid_got_zero )
  {
    cpuid_got_ptr_1 = cpuid_got_ptr;
    got_value = *(QWORD *)cpuid_got_ptr;
    // replace with Llzma_delta_props_encoder (backdoor_init_stage2)
    *(QWORD *)cpuid_got_ptr = (char *)*(&Llzma_block_buffer_decode_0 + 2) + runtime_offset;
    // this calls Llzma_delta_props_encoder due to the GOT overwrite
    runtime_offset = cpuid((unsigned int)ctx, prev_got_ptr, cpuid_got_ptr, &Llzma_block_buffer_decode_0, v2);
    // restore original
    *cpuid_got_ptr_1 = got_value;
  }
  return runtime_offset;
}








防护建议








使用检测脚本检查服务器是否满足漏洞环境标准,终端更新山石最新情报库。








关于山石情报中心








山石网科情报中心,涵盖威胁情报狩猎运维和入侵检测与防御团队。 山石网科情报中心专注于保护数字世界的安全。以情报狩猎、攻击溯源和威胁分析为核心,团队致力于预防潜在攻击、应对安全事件。山石网科情报中心汇集网络安全、计算机科学、数据分析等专家,多学科融合确保全面的威胁分析。我们积极创新,采用新工具和技术提升分析效率。团队协同合作,分享信息与见解,追求卓越,为客户保驾护航。无论是防范未来威胁还是应对当下攻击,我们努力确保数字世界安全稳定。其中山石网科网络入侵检测防御系统,是山石网科公司结合多年应用安全的攻防理论和应急响应实践经验积累的基础上自主研发完成,满足各类法律法规如 PCI、等级保护、企业内部控制规范等要求。








附件








附件一:自动解压缩代码

import struct
from dataclasses import dataclass
from typing import List

@dataclass
class next_state:
    delta_actions: int
    delta_mask: int

@dataclass
class final_state:
    result_code: int

def parse_action(statedesc: bytes):
    (dm_flags, da) = struct.unpack("<HH", statedesc)
    dm = dm_flags & ~7
    flags = dm_flags & 7
    if flags & 4:
        return final_state(da)
    if flags & 2:
        pass
    else:
        da = -da
    if flags & 1:
        pass
    else:
        dm = -dm
    return next_state(delta_actions = da-4, delta_mask = dm-16)

def parse_actiontable(serialized: bytes):
    while True:
        state = serialized[:4]
        if not state:
            return
        serialized = serialized[4:]
        yield parse_action(state)

def parse_mask(serialized: bytes) -> str:
    binmask = int.from_bytes(serialized, "little")
    result = ""
    for i in range(128):
        if binmask & (1 << i):
            result += chr(i)
    return result

class automaton:
    def __init__(self, maskdata : bytes, actiondata : bytes):
        self.maskdata = maskdata
        self.actiondata = actiondata

    def all_strings(self):
        init_mask_cursor = len(self.maskdata)-16
        init_mask = parse_mask(self.maskdata[-16:])
        init_action_cursor = len(self.actiondata)-4*len(init_mask)
        yield from self._all_recursive("", init_mask_cursor, init_action_cursor)

    def _all_recursive(self, prefix, mask_cursor, action_cursor):
        mask = parse_mask(self.maskdata[mask_cursor:mask_cursor+16])
        actions = parse_actiontable(self.actiondata[action_cursor:action_cursor + 4*len(mask)])
        for (c, action) in zip(mask, actions):
            string = prefix + c
            if isinstance(action, final_state):
                yield (action.result_code, string)
            else:
                yield from self._all_recursive(string, mask_cursor + action.delta_mask, action_cursor + action.delta_actions)

def load_malware_sample():
    from elftools.elf.elffile import ELFFile
    with open("liblzma_la-crc64-fast.o.this-is-malware""rb"as malware_object:
        malware_elf = ELFFile(malware_object)
        mask_data = malware_elf.get_section_by_name(".rodata.crc64_clmul1").data()
        action_data = malware_elf.get_section_by_name(".rodata.lzip_decode0").data()
        return automaton(mask_data, action_data)
        

def main():
    a = load_malware_sample()
    for (id, string) in a.all_strings():
        print(f"{id:4x}{repr(string)}")


if __name__ == "__main__":
    main()

原文始发于微信公众号(山石网科安全技术研究院):xz liblzma 供应链CVE-2024-3094分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月3日18:40:37
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   xz liblzma 供应链CVE-2024-3094分析https://cn-sec.com/archives/2627687.html

发表评论

匿名网友 填写信息