ELF二进制文件通用劫持技术探索

admin 2023年10月20日12:07:13评论32 views字数 25393阅读84分38秒阅读模式

概述



山石网科情报中心在日常恶意样本分析对抗中,发现linux下恶意劫持样本数量逐渐变多。在 Linux 和许多其他类Unix系统中,二进制文件通常使用 ELF(Executable and Linkable Format,可执行和可链接格式)格式。这种格式支持多种类型的二进制文件,包括可执行文件(.elf)、共享库文件(.so)、目标文件(.o)和内核模块文件(.ko)。
本期我们将不使用通用的PT_NOTE感染方式,由浅入深探索ELF下二进制文件劫持技术,即怎么把我们的恶意代码插入到ELF二进制文件中,同时给出对抗思路。
           

利用劫持技术实现恶意代码执行



 “劫持”的思路其实就是把目标函数地址改成我们想让其执行的代码位置,下面通过一个没有重定位的demo演示一下劫持思路和效果。测试文件:
// testlib.h
void func1();
void func2();

// testlib.c
#include<stdio.h>
#include "testlib.h"

void func1(){
        printf("test 1 func1n");
}

void func2(){
        printf("this is func2n");
}

// main.c
#include "testlib.h"
int main(){
        func1();
}

编译各个文件如下:
gcc -c testlib.c -o testlib.o -fPIC
gcc -shared testlib.o -o testlib.so
gcc main.c ./testlib.so -o main

那么执行./main函数将返回test 1 func1。现在我们尝试在共享库中修改func1的地址使其导向func2函数。

通过radare查看编译的共享库导出函数地址:0x00001109(func1)、0x0000111f(func2)
└─$ r2 -A testlib.so            
Cannot determine entrypoint, using 0x00001050.            
Warning: run r2 with -e bin.cache=true to fix relocations in disassembly            
[x] Analyze all flags starting with sym. and entry0 (aa)            
[x] Analyze function calls (aac)            
[x] Analyze len bytes of instructions for references (aar)            
[x] Finding and parsing C++ vtables (avrr)            
[x] Type matching analysis for all functions (aaft)            
[x] Propagate noreturn information (aanr)            
[x] Use -AA or aaaa to perform additional experimental analysis.            
[0x00001050]> iE            
[Exports]            
           
nth paddr      vaddr      bind   type size lib name            
―――――――――――――――――――――――――――――――――――――――――――――――――――            
6   0x00001109 0x00001109 GLOBAL FUNC 22       func1            
7   0x0000111f 0x0000111f GLOBAL FUNC 22       func2

可以定位到导出函数地址查看反汇编上下文如下:
[0x00001050]> s 0x00001109
[0x00001109]> pd
┌ 22: sym.func1 ();
│           0x00001109      55             push rbp
│           0x0000110a      4889e5         mov rbp, rsp
│           0x0000110d      488d05ec0e00.  lea rax, str.test_1_func1   ; segment.LOAD2
│                                                                      ; 0x2000 ; "test 1 func1"             
│           0x00001114      4889c7         mov rdi, rax                ; const char *s
│           0x00001117      e814ffffff     call sym.imp.puts           ; int puts(const char *s)
│           0x0000111c      90             nop
│           0x0000111d      5d             pop rbp
└           0x0000111e      c3             ret
┌ 22: sym.func2 ();
│           0x0000111f      55             push rbp
│           0x00001120      4889e5         mov rbp, rsp
│           0x00001123      488d05e30e00.  lea rax, str.this_is_func2  ; 0x200d ; "this is func2"
│           0x0000112a      4889c7         mov rdi, rax                ; const char *s
│           0x0000112d      e8fefeffff     call sym.imp.puts           ; int puts(const char *s)
│           0x00001132      90             nop
│           0x00001133      5d             pop rbp
└           0x00001134      c3             ret

在patch func1函数地址前,需要先找到.dynsym区段,可通过iS,或者使用readelf -S testlib.so,anyway:
[0x00000348]> iS
[Sections]

nth paddr        size vaddr       vsize perm name
―――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000000    0x0 0x00000000    0x0 ---- 
1   0x00000238   0x24 0x00000238   0x24 -r-- .note.gnu.build-id
2   0x00000260   0x28 0x00000260   0x28 -r-- .gnu.hash
3   0x00000288   0xc0 0x00000288   0xc0 -r-- .dynsym
4   0x00000348   0x7c 0x00000348   0x7c -r-- .dynstr
5   0x000003c4   0x10 0x000003c4   0x10 -r-- .gnu.version
6   0x000003d8   0x20 0x000003d8   0x20 -r-- .gnu.version_r
7   0x000003f8   0xa8 0x000003f8   0xa8 -r-- .rela.dyn
8   0x000004a0   0x18 0x000004a0   0x18 -r-- .rela.plt
9   0x00001000   0x17 0x00001000   0x17 -r-x .init
10  0x00001020   0x20 0x00001020   0x20 -r-x .plt
11  0x00001040    0x8 0x00001040    0x8 -r-x .plt.got
12  0x00001050   0xe5 0x00001050   0xe5 -r-x .text
13  0x00001138    0x9 0x00001138    0x9 -r-x .fini
14  0x00002000   0x1b 0x00002000   0x1b -r-- .rodata
15  0x0000201c   0x2c 0x0000201c   0x2c -r-- .eh_frame_hdr
16  0x00002048   0x9c 0x00002048   0x9c -r-- .eh_frame
17  0x00002df8    0x8 0x00003df8    0x8 -rw- .init_array
18  0x00002e00    0x8 0x00003e00    0x8 -rw- .fini_array
19  0x00002e08  0x1c0 0x00003e08  0x1c0 -rw- .dynamic
20  0x00002fc8   0x20 0x00003fc8   0x20 -rw- .got
21  0x00002fe8   0x20 0x00003fe8   0x20 -rw- .got.plt
22  0x00003008    0x8 0x00004008    0x8 -rw- .data
23  0x00003010    0x0 0x00004010    0x8 -rw- .bss
24  0x00003010   0x1f 0x00000000   0x1f ---- .comment
25  0x00003030  0x288 0x00000000  0x288 ---- .symtab
26  0x000032b8  0x181 0x00000000  0x181 ---- .strtab
27  0x00003439   0xf1 0x00000000   0xf1 ---- .shstrtab

vaddr:0x00000288,定位到该地址查看hex值,能够看到小端地址 0911 0000 0000,这就是我们的目标:
[0x00000348]> s 0x288
[0x00000288]> px
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000288  0000 0000 0000 0000 0000 0000 0000 0000  ................                                       
0x00000298  0000 0000 0000 0000 1000 0000 2000 0000  ............ ...
0x000002a8  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x000002b8  5b00 0000 1200 0000 0000 0000 0000 0000  [...............
0x000002c8  0000 0000 0000 0000 0100 0000 2000 0000  ............ ...
0x000002d8  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x000002e8  2c00 0000 2000 0000 0000 0000 0000 0000  ,... ...........
0x000002f8  0000 0000 0000 0000 4600 0000 2200 0000  ........F..."...
0x00000308  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x00000318  5500 0000 1200 0c00 0911 0000 0000 0000  U...............
0x00000328  1600 0000 0000 0000 6000 0000 1200 0c00  ........`.......
0x00000338  1f11 0000 0000 0000 1600 0000 0000 0000  ................
0x00000348  005f 5f67 6d6f 6e5f 7374 6172 745f 5f00  .__gmon_start__.
0x00000358  5f49 544d 5f64 6572 6567 6973 7465 7254  _ITM_deregisterT
0x00000368  4d43 6c6f 6e65 5461 626c 6500 5f49 544d  MCloneTable._ITM
0x00000378  5f72 6567 6973 7465 7254 4d43 6c6f 6e65  _registerTMClone

为了方便查看,跟进偏移位置:
[0x00000288]> s 0x318 + 8
[0x00000320]> px
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000320  0911 0000 0000 0000 1600 0000 0000 0000  ................                                       
0x00000330  6000 0000 1200 0c00 1f11 0000 0000 0000  `...............

符号节表每一个条目结构如下:
typedef struct {
        Elf64_Word      st_name;
        unsigned char   st_info;
        unsigned char   st_other;
        Elf64_Half      st_shndx;
        Elf64_Addr      st_value;
        Elf64_Xword     st_size;
} Elf64_Sym;

现在patch上func2地址并退出:
[0x00000320]> wv 0x0000111f
[0x00000320]> px
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000320  1f11 0000 0000 0000 1600 0000 0000 0000  ................                                      
0x00000330  6000 0000 1200 0c00 1f11 0000 0000 0000  `...............


攻击效果



执行main:
└─$ ./main            
this is func2
可以看到代码已经执行我们的目标函数“func2”了,这就是劫持的过程。当然上面的情况是没有重定位下,所以接下来我们看一下存在重定位的处理方案。

           

利用动态重定位实现恶意代码执行




我们按照上述基础思路进行劫持,考虑如下测试文件test_more.c:
#include<stdio.h>
__attribute__((constructor)) void msg(int argc, char **argv) {
   printf("hello from msg() constructorn");
}

__attribute__((constructor)) void second() {
   printf("hello from second() constructorinn");
}

void not_called() {
   puts("I should have never been calledn");
}
int main() {
   puts("hello from main -- hopefully al constructors were called.in");return 0;
}

编译gcc test_more.c -o test_more运行效果如下:
─$ ./test_more
hello from msg() constructor
hello from second() constructorin
hello from main -- hopefully al constructors were called.in

__attribute__((constructor))是在main函数之前执行一个函数。在老版本中函数执行顺序在区段 .ctors和 .init 中,后面更高的版本被 .init_array 和 dynamic tag 的 DT_INIT_ARRAY 条目取代。该数组由函数指针条目组成,每个指针都指向将在 main 函数之前执行的构造函数例程。我们先查看第一个函数的入口处地址:
└─$ objdump -D test_more | grep msg             
0000000000001139 <msg>:

查看init_array区段信息,能够看到0x3dc8小段地址保存着 39 11:
└─$ objdump -D test_more | grep init_array -A 20
Disassembly of section .init_array:

0000000000003dc0 <__frame_dummy_init_array_entry>:
    3dc0:       30 11                   xor    %dl,(%rcx)
    3dc2:       00 00                   add    %al,(%rax)
    3dc4:       00 00                   add    %al,(%rax)
    3dc6:       00 00                   add    %al,(%rax)
    3dc8:       39 11                   cmp    %edx,(%rcx)
    3dca:       00 00                   add    %al,(%rax)
    3dcc:       00 00                   add    %al,(%rax)
    3dce:       00 00                   add    %al,(%rax)
    3dd0:       5a                      pop    %rdx
    3dd1:       11 00                   adc    %eax,(%rax)
    3dd3:       00 00                   add    %al,(%rax)
    3dd5:       00 00                   add    %al,(%rax)
         
现在开始patch,用not_call函数地址替换第一个msg函数,动作同上,r2抄起来,找到init_array地址:
[0x00001050]> iS | grep init_array
20  0x00002dc0   0x18 0x00003dc0   0x18 -rw- .init_array

找到 not_called 函数地址:
[0x00003dc0]> is | grep not_called
22  0x00001170 0x00001170 GLOBAL FUNC   22       not_called
[0x00003dc0]> s 0x00003dc0 + 8
[0x00003dc8]> px 10
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00003dc8  3911 0000 0000 0000 5a11                 9.......Z.                                                                               
[0x00003dc8]> wx 0x70110000
[0x00003dc8]> pv
0x0000000000001170
[0x00003dc8]> 

替换后执行发现没有生效:
└─$ ./test_more                                 
hello from msg() constructor
hello from second() constructorin
hello from main -- hopefully al constructors were called.in  

现在使用GDB动态调试看看,在_start处设置断点,并运行到断点处,使用maintenance info sections,用于查看调试会话中的程序的各个段(sections)的信息。段是程序的内存分布中的逻辑块,包括代码段、数据段、堆、栈等等:
(gdb) b _start
Breakpoint 1 at 0x1060
(gdb) r
Starting program: /home/jentle/ELF_Infect/test_more 

Breakpoint 1, 0x0000555555555060 in _start ()
(gdb) maintenance info sections 
Exec file:
    `/home/jentle/ELF_Infect/test_more', file type elf64-x86-64.
 [0]      0x555555554318->0x555555554334 at 0x00000318: .interp ALLOC LOAD READONLY DATA HAS_CONTENTS
 [1]      0x555555554338->0x555555554358 at 0x00000338: .note.gnu.property ALLOC LOAD READONLY DATA HAS_CONTENTS
 [2]      0x555555554358->0x55555555437c at 0x00000358: .note.gnu.build-id ALLOC LOAD READONLY DATA HAS_CONTENTS
 [3]      0x55555555437c->0x55555555439c at 0x0000037c: .note.ABI-tag ALLOC LOAD READONLY DATA HAS_CONTENTS
 [4]      0x5555555543a0->0x5555555543c4 at 0x000003a0: .gnu.hash ALLOC LOAD READONLY DATA HAS_CONTENTS
 [5]      0x5555555543c8->0x555555554470 at 0x000003c8: .dynsym ALLOC LOAD READONLY DATA HAS_CONTENTS
 [6]      0x555555554470->0x5555555544f2 at 0x00000470: .dynstr ALLOC LOAD READONLY DATA HAS_CONTENTS
 [7]      0x5555555544f2->0x555555554500 at 0x000004f2: .gnu.version ALLOC LOAD READONLY DATA HAS_CONTENTS
 [8]      0x555555554500->0x555555554520 at 0x00000500: .gnu.version_r ALLOC LOAD READONLY DATA HAS_CONTENTS
 [9]      0x555555554520->0x555555554610 at 0x00000520: .rela.dyn ALLOC LOAD READONLY DATA HAS_CONTENTS
 [10]     0x555555554610->0x555555554628 at 0x00000610: .rela.plt ALLOC LOAD READONLY DATA HAS_CONTENTS
 [11]     0x555555555000->0x55555555501b at 0x00001000: .init ALLOC LOAD READONLY CODE HAS_CONTENTS
 [12]     0x555555555020->0x555555555040 at 0x00001020: .plt ALLOC LOAD READONLY CODE HAS_CONTENTS
 [13]     0x555555555040->0x555555555050 at 0x00001040: .plt.got ALLOC LOAD READONLY CODE HAS_CONTENTS
 [14]     0x555555555050->0x555555555060 at 0x00001050: .plt.sec ALLOC LOAD READONLY CODE HAS_CONTENTS
 [15]     0x555555555060->0x555555555235 at 0x00001060: .text ALLOC LOAD READONLY CODE HAS_CONTENTS
 [16]     0x555555555238->0x555555555245 at 0x00001238: .fini ALLOC LOAD READONLY CODE HAS_CONTENTS
 [17]     0x555555556000->0x5555555560b4 at 0x00002000: .rodata ALLOC LOAD READONLY DATA HAS_CONTENTS
 [18]     0x5555555560b4->0x555555556110 at 0x000020b4: .eh_frame_hdr ALLOC LOAD READONLY DATA HAS_CONTENTS
 [19]     0x555555556110->0x555555556278 at 0x00002110: .eh_frame ALLOC LOAD READONLY DATA HAS_CONTENTS
 [20]     0x555555557da8->0x555555557dc0 at 0x00002da8: .init_array ALLOC LOAD DATA HAS_CONTENTS
 [21]     0x555555557dc0->0x555555557dc8 at 0x00002dc0: .fini_array ALLOC LOAD DATA HAS_CONTENTS
 [22]     0x555555557dc8->0x555555557fb8 at 0x00002dc8: .dynamic ALLOC LOAD DATA HAS_CONTENTS
 [23]     0x555555557fb8->0x555555558000 at 0x00002fb8: .got ALLOC LOAD DATA HAS_CONTENTS
 [24]     0x555555558000->0x555555558010 at 0x00003000: .data ALLOC LOAD DATA HAS_CONTENTS
 [25]     0x555555558010->0x555555558018 at 0x00003010: .bss ALLOC
 [26]     0x00000000->0x00000029 at 0x00003010: .comment READONLY HAS_CONTENTS

我们在init_array区段偏移8Bytes位置设置监视断点(Tips🚩),watch *(long *)0x555555557db0:
(gdb) watch *(long *)0x555555557db0
Hardware watchpoint 2: *(long *)0x555555557db0
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/jentle/ELF_Infect/test_more 

Hardware watchpoint 2: *(long *)0x555555557db0

Old value = 4482
New value = 93824992235849  // 0x555555555149
0x00007ffff7fdd307 in ?? () from /lib64/ld-linux-x86-64.so.2
(gdb) hex(4482)

4482(0x1182)系patch的not_called函数地址(两次实验可能结果和上面不一样)被修改了,并且程序停止在”/lib64/ld-linux-x86-64.so.2“,
(gdb) info registers 
rax            0x555555554550      93824992232784
rbx            0x555555554598      93824992232856
rcx            0x555555557db0      93824992247216
rdx            0x555555555149      93824992235849
rsi            0x7ffff7ffd9e8      140737354127848
rdi            0x7fffffffdc40      140737488346176
rbp            0x7fffffffdcb0      0x7fffffffdcb0
rsp            0x7fffffffdbb0      0x7fffffffdbb0
r8             0x0                 0
r9             0x7ffff7fcf670      140737353938544
r10            0x0                 0
r11            0x7ffff7ffe190      140737354129808
r12            0x0                 0
r13            0x0                 0
r14            0x0                 0
r15            0x555555554000      93824992231424
rip            0x7ffff7fdd307      0x7ffff7fdd307
eflags         0x202               [ IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
(gdb) x/30i 0x7ffff7fdd300
   0x7ffff7fdd300: sbb    %cl,-0x6(%rcx,%rax,1)
   0x7ffff7fdd304: mov    %rdx,(%rcx)
=> 0x7ffff7fdd307: cmp    %rax,%rbx
   0x7ffff7fdd30a: ja     0x7ffff7fdd2e0
   0x7ffff7fdd30c: mov    0x1d0(%r11),%r10
   0x7ffff7fdd313: test   %r10,%r10

查看相关参数:
(gdb) info symbol 0x555555555149
msg in section .text of /home/jentle/ELF_Infect/test_more
(gdb) disas msg
Dump of assembler code for function msg:
   0x0000555555555149 <+0>: endbr64 
   0x000055555555514d <+4>: push   %rbp
   0x000055555555514e <+5>: mov    %rsp,%rbp
   0x0000555555555151 <+8>: sub    $0x10,%rsp
   0x0000555555555155 <+12>: mov    %edi,-0x4(%rbp)
   0x0000555555555158 <+15>: mov    %rsi,-0x10(%rbp)
   0x000055555555515c <+19>: lea    0xea5(%rip),%rdi        # 0x555555556008
   0x0000555555555163 <+26>: callq  0x555555555050 <puts@plt>
   0x0000555555555168 <+31>: nop
   0x0000555555555169 <+32>: leaveq 
   0x000055555555516a <+33>: retq   
End of assembler dump.

init_array 用于存储需要在程序启动时执行的函数指针数组,当 ELF 文件被加载到内存中并启动时,系统会调用 _start 函数(或类似的入口函数),该函数负责执行初始化代码并调用 init_array 中的所有函数。从这个分析中,我们可以得出结论,.init_array 中的任何偏移量都将在运行时被覆盖。其次覆盖 .init_array 中的偏移量发生在dynamic/runtime链接器和加载程序代码中。前面,我们提到共享对象映射到进程地址空间。内核创建进程的映像后,它将信息放入进程(堆栈)的内存中在称为辅助向量的结构中,并将执行转移到dynamic/runtime链接器和装载机。然后,它(动态/运行时链接器和加载程序)将使用此信息进一步填充进程映像。

对抗方案

 


针对上述情况,动态链接库的关键任务之一就是重定位,我们可以使用LD_DEBUG命令以指定要显示的动态链接器调试信息,使用下面两个类别查看:
  1. reloc:这个调试类别会显示与重定位(relocation)相关的信息。重定位是指动态链接器将共享库加载到内存中并调整共享库中的符号引用,以使它们指向正确的地址。reloc 类别将显示关于重定位的详细信息,包括哪些符号被重定位以及它们被重定位到哪里。
  2. statistics:这个调试类别会显示与动态链接器加载和符号解析的统计信息。它提供有关程序加载过程的性能统计数据,如加载时间、符号解析时间等。
jentle@jentle-ubuntu-20:~/ELF_Infect$ LD_DEBUG=reloc,statistics ./test_more
     20465: 
     20465: relocation processing: /lib/x86_64-linux-gnu/libc.so.6 (lazy)
     20465: 
     20465: relocation processing: ./test_more
     20465: 
     20465: relocation processing: /lib64/ld-linux-x86-64.so.2
     20465: 
     20465: runtime linker statistics:
     20465:   total startup time in dynamic loader: 125692 cycles
     20465:             time needed for relocation: 37496 cycles (29.8%)
     20465:                  number of relocations: 90
     20465:       number of relocations from cache: 3
     20465:         number of relative relocations: 1258
     20465:            time needed to load objects: 45073 cycles (35.8%)
     20465: 
     20465: calling init: /lib/x86_64-linux-gnu/libc.so.6
     20465: 
     20465: 
     20465: initialize program: ./test_more
     20465: 
hello from msg() constructor
hello from second() constructorin
     20465: 
     20465: transferring control: ./test_more
     20465: 
hello from main -- hopefully al constructors were called.in
     20465: 
     20465: calling fini: ./test_more [0]
     20465: 
     20465: 
     20465: runtime linker statistics:
     20465:            final number of relocations: 92
     20465: final number of relocations from cache: 3

现在我们可以查看重定位条目来揭开 .init_array 发生的情况。 在下面的屏幕截图中,前五个相对重定位条目很重要,并且类型为 R_X86_64_RELATIVE。最后一列是偏移量,0x1149 是msg函数和构造函数所在位置:
jentle@jentle-ubuntu-20:~/ELF_Infect$ readelf -r test_more

Relocation section '.rela.dyn' at offset 0x520 contains 10 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000003da8  000000000008 R_X86_64_RELATIVE                    1140
000000003db0  000000000008 R_X86_64_RELATIVE                    1149
000000003db8  000000000008 R_X86_64_RELATIVE                    116b
000000003dc0  000000000008 R_X86_64_RELATIVE                    1100
000000004008  000000000008 R_X86_64_RELATIVE                    4008
000000003fd8  000100000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0
000000003fe0  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000003fe8  000400000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000003ff0  000500000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTa + 0
000000003ff8  000600000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize@GLIBC_2.2.5 + 0

Relocation section '.rela.plt' at offset 0x610 contains 1 entry:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000003fd0  000200000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0

让我们尝试修改 msg 函数和构造函数的重定位条目来执行我们的 not_used 函数。我们可以首先将二进制文件重新加载到“r2”中,然后找到 rela.dyn 部分,寻找该部分的开头并读取条目的十六进制转储输出,每一个rela结构是24bytes,所以我们pass第一个条目,找第二个结构中16bytes的的偏移地址,+40
typedef struct {
        Elf32_Addr      r_offset;
        Elf32_Word      r_info;
        Elf32_Sword     r_addend;
} Elf32_Rela;
[0x00001060]> iS
[Sections]

nth paddr        size vaddr       vsize perm name
―――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00000000    0x0 0x00000000    0x0 ---- 
1   0x00000318   0x1c 0x00000318   0x1c -r-- .interp
2   0x00000338   0x20 0x00000338   0x20 -r-- .note.gnu.property
3   0x00000358   0x24 0x00000358   0x24 -r-- .note.gnu.build_id
4   0x0000037c   0x20 0x0000037c   0x20 -r-- .note.ABI_tag
5   0x000003a0   0x24 0x000003a0   0x24 -r-- .gnu.hash
6   0x000003c8   0xa8 0x000003c8   0xa8 -r-- .dynsym
7   0x00000470   0x82 0x00000470   0x82 -r-- .dynstr
8   0x000004f2    0xe 0x000004f2    0xe -r-- .gnu.version
9   0x00000500   0x20 0x00000500   0x20 -r-- .gnu.version_r
10  0x00000520   0xf0 0x00000520   0xf0 -r-- .rela.dyn
11  0x00000610   0x18 0x00000610   0x18 -r-- .rela.plt
12  0x00001000   0x1b 0x00001000   0x1b -r-x .init
13  0x00001020   0x20 0x00001020   0x20 -r-x .plt
14  0x00001040   0x10 0x00001040   0x10 -r-x .plt.got
15  0x00001050   0x10 0x00001050   0x10 -r-x .plt.sec
16  0x00001060  0x1d5 0x00001060  0x1d5 -r-x .text
17  0x00001238    0xd 0x00001238    0xd -r-x .fini
18  0x00002000   0xb4 0x00002000   0xb4 -r-- .rodata
19  0x000020b4   0x5c 0x000020b4   0x5c -r-- .eh_frame_hdr
20  0x00002110  0x168 0x00002110  0x168 -r-- .eh_frame
21  0x00002da8   0x18 0x00003da8   0x18 -rw- .init_array
22  0x00002dc0    0x8 0x00003dc0    0x8 -rw- .fini_array
23  0x00002dc8  0x1f0 0x00003dc8  0x1f0 -rw- .dynamic
24  0x00002fb8   0x48 0x00003fb8   0x48 -rw- .got
25  0x00003000   0x10 0x00004000   0x10 -rw- .data
26  0x00003010    0x0 0x00004010    0x8 -rw- .bss
27  0x00003010   0x29 0x00000000   0x29 ---- .comment
28  0x00003040  0x660 0x00000000  0x660 ---- .symtab
29  0x000036a0  0x21d 0x00000000  0x21d ---- .strtab
30  0x000038bd  0x11a 0x00000000  0x11a ---- .shstrtab


[0x00001060]>
 s 0x520
[0x00000520]> px
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000520  a83d 0000 0000 0000 0800 0000 0000 0000  .=..............
0x00000530  4011 0000 0000 0000 b03d 0000 0000 0000  @........=......
0x00000540  0800 0000 0000 0000 4911 0000 0000 0000  ........I.......
0x00000550  b83d 0000 0000 0000 0800 0000 0000 0000  .=..............
0x00000560  6b11 0000 0000 0000 c03d 0000 0000 0000  k........=......
0x00000570  0800 0000 0000 0000 0011 0000 0000 0000  ................
0x00000580  0840 0000 0000 0000 0800 0000 0000 0000  .@..............
0x00000590  0840 0000 0000 0000 d83f 0000 0000 0000  .@.......?......
0x000005a0  0600 0000 0100 0000 0000 0000 0000 0000  ................
0x000005b0  e03f 0000 0000 0000 0600 0000 0300 0000  .?..............
0x000005c0  0000 0000 0000 0000 e83f 0000 0000 0000  .........?......
0x000005d0  0600 0000 0400 0000 0000 0000 0000 0000  ................
0x000005e0  f03f 0000 0000 0000 0600 0000 0500 0000  .?..............
0x000005f0  0000 0000 0000 0000 f83f 0000 0000 0000  .........?......
0x00000600  0600 0000 0600 0000 0000 0000 0000 0000  ................
0x00000610  d03f 0000 0000 0000 0700 0000 0200 0000  .?..............
[0x00000520]> s + 40
[0x00000548]> px
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000548  4911 0000 0000 0000 b83d 0000 0000 0000  I........=......
0x00000558  0800 0000 0000 0000 6b11 0000 0000 0000  ........k.......
0x00000568  c03d 0000 0000 0000 0800 0000 0000 0000  .=..............
0x00000578  0011 0000 0000 0000 0840 0000 0000 0000  .........@......
0x00000588  0800 0000 0000 0000 0840 0000 0000 0000  .........@......
0x00000598  d83f 0000 0000 0000 0600 0000 0100 0000  .?..............
0x000005a8  0000 0000 0000 0000 e03f 0000 0000 0000  .........?......
0x000005b8  0600 0000 0300 0000 0000 0000 0000 0000  ................
0x000005c8  e83f 0000 0000 0000 0600 0000 0400 0000  .?..............
0x000005d8  0000 0000 0000 0000 f03f 0000 0000 0000  .........?......
0x000005e8  0600 0000 0500 0000 0000 0000 0000 0000  ................
0x000005f8  f83f 0000 0000 0000 0600 0000 0600 0000  .?..............
0x00000608  0000 0000 0000 0000 d03f 0000 0000 0000  .........?......
0x00000618  0700 0000 0200 0000 0000 0000 0000 0000  ................
0x00000628  ffff ffff ffff ffff ffff ffff ffff ffff  ................
0x00000638  ffff ffff ffff ffff ffff ffff ffff ffff  ................
[0x00000548]> 


攻击效果

  


把偏移地址换成我们的not_called:
jentle@jentle-ubuntu-20:~/ELF_Infect$ objdump -D test_more | grep not_called
0000000000001182 <not_called>:

退出并执行将会看到运行成功:
[0x00000548]> wx 0x821100000000
[0x00000548]> px
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000548  8211 0000 0000 0000 b83d 0000 0000 0000  .........=......
0x00000558  0800 0000 0000 0000 6b11 0000 0000 0000  ........k.......
0x00000568  c03d 0000 0000 0000 0800 0000 0000 0000  .=..............
0x00000578  0011 0000 0000 0000 0840 0000 0000 0000  .........@......
0x00000588  0800 0000 0000 0000 0840 0000 0000 0000  .........@......
0x00000598  d83f 0000 0000 0000 0600 0000 0100 0000  .?..............
0x000005a8  0000 0000 0000 0000 e03f 0000 0000 0000  .........?......
0x000005b8  0600 0000 0300 0000 0000 0000 0000 0000  ................
0x000005c8  e83f 0000 0000 0000 0600 0000 0400 0000  .?..............
0x000005d8  0000 0000 0000 0000 f03f 0000 0000 0000  .........?......
0x000005e8  0600 0000 0500 0000 0000 0000 0000 0000  ................
0x000005f8  f83f 0000 0000 0000 0600 0000 0600 0000  .?..............
0x00000608  0000 0000 0000 0000 d03f 0000 0000 0000  .........?......
0x00000618  0700 0000 0200 0000 0000 0000 0000 0000  ................
0x00000628  ffff ffff ffff ffff ffff ffff ffff ffff  ................
0x00000638  ffff ffff ffff ffff ffff ffff ffff ffff  ................
[0x00000548]> q
jentle@jentle-ubuntu-20:~/ELF_Infect$ ./test_more 
I should have never been called

hello from second() constructorin
hello from main -- hopefully al constructors were called.in
发现已经成功替换了代码执行顺序,并且不受重定位影响。 根据上面的过程我们发现可以不修改程序的入口点,而是修改重定位结构里面的偏移量来帮助我们”infect“,即重定位表劫持技术。


总结



通过如上演示,我们现在可以利用相对重定位来定位任何 ELF 二进制文件,包括标准可执行文件和库(共享对象)。所以ELF感染方法如PT_NOTE到PT_LOAD和 文本段填充,曾经用于定位标准 ELF 可执行文件,现在可以应用于 ELF 共享对象可执行文件。然后,任何链接到受感染共享库的ELF二进制文件都将携带恶意代码。针对此技术,山石网科情报中心给出如下预防措施:
  1. 签名和校验文件:使用数字签名来验证可执行文件的完整性。只有受信任的签名可执行文件才能运行。这可以通过工具如GnuPG或X.509证书来实现。
  2. 使用只读文件系统:将关键系统目录和文件系统设置为只读,以防止攻击者修改文件。
  3. 保持系统更新:及时安装操作系统和应用程序的安全更新,以填补已知漏洞。
  4. 使用文件系统完整性检查:工具如Tripwire、AIDE(Advanced Intrusion Detection Environment)或OSSEC可以监视系统文件的完整性,以检测未经授权的更改。
  5. 限制文件权限:确保只有授权用户可以修改系统文件和可执行文件。使用适当的文件和目录权限设置(chmod、chown)
参考项目:
GitHub - sad0p/d0zer: Elf binary infector written in Go. (https://github.com/sad0p/d0zer)
    

关于山石网科情报中心



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

原文始发于微信公众号(山石网科安全技术研究院):ELF二进制文件通用劫持技术探索

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年10月20日12:07:13
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   ELF二进制文件通用劫持技术探索https://cn-sec.com/archives/2125075.html

发表评论

匿名网友 填写信息