Unix系列(15)IFUNC机制初探

admin 2024年4月19日23:38:25评论8 views字数 3848阅读12分49秒阅读模式
创建: 2024-03-29 10:00
修改: 2024-04-08 15:40
https://scz.617.cn/unix/202403291000.txt

目录:

☆ 背景介绍
☆ 测试代码
    1) some.c
    2) ifunctest.c
☆ 整体流程框架
☆ liblzma后门
    2) 第一步Hook
    3) IFUNC机制的作用
☆ 参考资源

☆ 背景介绍

参看

《liblzma后门疑似国家级APT》

https://scz.617.cn/unix/202403290900.txt

某些版本liblzma.so被植入后门代码,被全世界的安全人员鞭尸式分析,目前已知其包含但不限于如下功能:

Command 0x00    Unknown
Command 0x01    SSH authentication bypass
Command 0x02    Execute shell command
Command 0x03    Execute shell command with specified UID/GID

即是说,不只是远程代码执行,也确有登录认证绕过。其后门协议涉及Ed448椭圆曲线签名算法,相应私钥只为作恶方所掌握。故,即便有暴露在公网的后门,除了作恶者,其他人无法利用该后门。已知相关PoC均需Patch恶意liblzma.so中Ed448公钥,仅有研究意义。

安全人员对后门功能研究得越来越深入、细致,围观即可。相比之下,我对后门第一步Hook如何完成更好奇些,后来ZYH、Lenny Wang分别回答了这个问题

本文从正常程序员角度初探IFUNC机制,与liblzma后门并非强相关,但也不是无关。

☆ 测试代码

1) some.c

/*
 * gcc -fPIC -shared -Wl,-soname,libsome.so -Wl,-m,elf_x86_64 -Wall -pipe -O0 -g3 -o libsome.so some.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

static void foo_0 ( void )
{
    printf( "call foo_0()n" );
}

__attribute__((used))
static void foo_1 ( void )
{
    printf( "call foo_1()n" );
}

__attribute__((used))
static void * foo_resolver ( void )
{
    printf( "call foo_resolver()n" );
    return ( void * )&foo_0;
}

extern void foo( void ) __attribute__((ifunc("foo_resolver")));

__attribute__((constructor))
static void some_init ( int argc, char **argv, char **envp )
{
    printf( "call some_init()n" );

}

some.c对应动态链接库libsome.so,只有名为foo的导出函数,其符号解析由foo_resolver完成,后者返回哪个函数指针,foo就对应哪个函数,foo_resolver这个符号并未导出。

常规导出函数是静态导出,链接时导出表已确定;IFUNC导出函数是动态导出,运行时由"ifunc resolver"决定导出谁,填写导出表。本例foo_resolver直接返回foo_0,实际中则是基于某种条件决定返回foo_0、foo_1中的某一个,后面会展示liblzma.so的"ifunc resolver"实现。

"ifunc resolver"的调用时机非常早期,其被调用时环境变量尚未就位,getenv()啥也取不到。换句话说,不要指望通过环境变量向"ifunc resolver"传递参数。

some_init与IFUNC机制无关,用于其他测试目的。

2) ifunctest.c

/*
 * gcc -Wall -pipe -O0 -g3 -o ifunctest ifunctest.c -L. -lsome
 *
 * LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./ifunctest
 */

#include <stdio.h>

extern void foo void );

int main int argc, char * argv[] )
{
    printf"call main()n" );
    foo();
    foo();
    return 0;
}

ifunctest是主程序,会动态链接libsome.so,导入来自后者的foo函数,本例实际导入foo_0。

$ LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH FOO_RESOLVER=1 ./ifunctest
call foo_resolver()
call some_init()
call main()
call foo_0()
call foo_0()

从printf结果看出,foo_resolver甚至比some_init还要早调用,不只是"before main"、"before _start",更是"before .init_array"。

☆ 整体流程框架

用GDB调试ifunctest,大概有下面这些流程:

Unix系列(15)IFUNC机制初探

一些关键节点的执行顺序:

ld-linux.so e_entry
ifunc resolver
.init_array[]
normal e_entry
normal main

☆ liblzma后门

2) 第一步Hook

From ZYH & Lenny Wang

第一步Hook通过修改源码完成,原来的代码是:

/*
 * xz-5.6.0srcliblzmacheckcrc64_fast.c
 */

#ifdef CRC_USE_IFUNC
extern LZMA_API(uint64_t)
lzma_crc64(const uint8_t *buf, size_t size, uint64_t crc)
        __attribute__((__ifunc__("crc64_resolve")))
;

static crc64_func_type
crc64_resolve(void)
{
    return is_arch_extension_supported()
            ? &crc64_arch_optimized : &crc64_generic;
}

liblzma.so动态导出符号lzma_crc64,加载时由crc64_resolve根据CPU情况决定该符号对应crc64_arch_optimized、crc64_generic中的某一个。crc64_resolve本身不是导出符号。

改过的代码是:

static crc64_func_type
crc64_resolve(void)
{
    /*
     * 前面多了个下划线
     */

    return _is_arch_extension_supported()
            ? &crc64_arch_optimized : &crc64_generic;
}

修改源码动作在injected.txt中。用了管道,改过的源码没有落盘,与恶意payload一起生成新的.o

3) IFUNC机制的作用

crc64_resolve是"ifunc resolver",加载liblzma.so时,会自动调用它,无需显式调用。其调用时机非常之早,比liblzma.so可能存在的.init_array[]还要早,在"catch load liblzma"命中之前就被调用了,这是一种超级"before _start"机制。换句话说,只要某个ELF直接、间接依赖liblzma.so,启动该ELF时,crc64_resolve就会得到执行机会。过去反入侵检测时会检查ELF的.init_array[],现在应该增加对"ifunc resolver"的检查,比如:

objdump -CT liblzma.so.5.6.0 | grep "g   iD"
nm -CD liblzma.so.5.6.0 | grep " i "
readelf -W --dyn-syms --demangle liblzma.so.5.6.0 | grep -E "IFUNC   GLOBAL DEFAULT"

可用IDA反汇编so,查看lzma_crc64,进而定位crc64_resolve。

☆ 参考资源

https://sourceware.org/glibc/wiki/GNU_IFUNC

xz/liblzma后门恶意代码注入方式分析 - Lenny Wang, UID(2045181921) [2024-04-03]
https://lennysec.github.io/xz-backdoor-code-injection-analysis/

原文始发于微信公众号(青衣十三楼飞花堂):Unix系列(15)--IFUNC机制初探

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月19日23:38:25
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Unix系列(15)IFUNC机制初探http://cn-sec.com/archives/2638179.html

发表评论

匿名网友 填写信息