使用字节跳动
HIDS
来检测ptrace进程注入
字节跳动HIDS
使用内核对象的方式挂钩内核里面的函数。它是使用了kprobe
来对ptrace
挂钩监控ptrace
。
kprobe
是内核中一种手段,可以动态插入到任意内核流程,收集调试和性能信息。
在典型情况下,基于KProbe
的插件被打包为内核模块。模块的init
函数安装(“注册”)一个或多个探针,而exit
函数注销它们。注册函数(如register_kprobe()
)指定插入探针的位置以及命中探针时要调用的处理程序。还有register_/unregister_*probes()
函数,用于批量注册/取消注册一组探针。当您必须一次注销大量探针时,这些函数可以加快注销过程。
基于kprobe
开发,根据include/linux/kprobes.h
的kprobe
定义
typedef int (*kprobe_pre_handler_t) (struct kprobe *, struct pt_regs *);
typedef void (*kprobe_post_handler_t) (struct kprobe *, struct pt_regs *,
unsigned long flags)
struct kprobe {
struct hlist_node hlist; /* list of kprobes for multi-handler support */
struct list_head list;
/*count the number of times this probe was temporarily disarmed */
unsigned long nmissed;
/* location of the probe point */
kprobe_opcode_t *addr;
/* Allow user to indicate symbol name of the probe point */
const char *symbol_name;
/* Offset into the symbol */
unsigned int offset;
/* Called before addr is executed. */
kprobe_pre_handler_t pre_handler;
/* Called after addr is executed, unless... */
kprobe_post_handler_t post_handler;
/* Saved opcode (which has been replaced with breakpoint) */
kprobe_opcode_t opcode;
/* copy of the original instruction */
struct arch_specific_insn ainsn;
/*
* Indicates various status flags.
* Protected by kprobe_mutex after this kprobe is registered.
*/
u32 flags;
};
需要定义pre_handler
或post_handler
和symbol_name
(挂钩的符号)
而字节跳动HIDS
在driver/LKM/src/smith_hook.c
定义了挂钩对象和处理函数
struct kprobe ptrace_kprobe = {
.symbol_name = P_GET_SYSCALL_NAME(ptrace),
.pre_handler = ptrace_pre_handler,
};
和注册/注销函数
int register_ptrace_kprobe(void)
{
int ret;
ret = register_kprobe(&ptrace_kprobe); if (ret == 0)
ptrace_kprobe_state = 0x1;
return ret;
}
void unregister_ptrace_kprobe(void)
{
unregister_kprobe(&ptrace_kprobe);
}
而这些钩子都是在这里控制
// Hook on-off
int CONNECT_HOOK = 0;
int BIND_HOOK = 0;
int EXECVE_HOOK = 0;
int CREATE_FILE_HOOK = 0;
int PTRACE_HOOK = 1;
int DO_INIT_MODULE_HOOK = 0;
int UPDATE_CRED_HOOK = 0;int RENAME_HOOK = 0;
int LINK_HOOK = 0;
int SETSID_HOOK = 0;
int PRCTL_HOOK = 0;
int MEMFD_CREATE_HOOK = 0;
int DNS_HOOK = 0;
int CALL_USERMODEHELPER = 0;
int ACCEPT_HOOK = 0;
int OPEN_HOOK = 0;
int MPROTECT_HOOK = 0;
int NANOSLEEP_HOOK = 0;
int KILL_HOOK = 0;
int RM_HOOK = 0;
int EXIT_HOOK = 0;
由于我们只要看ptrace
,所以把其它钩子都变为0.
编译和运行
make clean && make
insmod hids_driver.ko
看一下运行结果
[[email protected] LKM]# dmesg|grep "ELKEID"
[17127.251384] [ELKEID] Filter Init Success
[17127.275594] [ELKEID] SANDBOX: 0
[17127.276001] [ELKEID] register_kprobe success: connect_hook: 0,load_module_hook: 0,execve_hook: 0,call_usermodehekoer_hook: 0,bind_hook: 0,create_file_hook: 0,ptrace_hook: 1, update_cred_hook: 0, dns_hook: 0, accept_hook:0, mprotect_hook: 0,link_hook: 0, memfd_create: 0, rename_hook: 0,setsid_hook:0, prctl_hook:0, open_hook:0, nanosleep_hook:0, kill_hook: 0, rm_hook: 0, EXIT_HOOK: 0, EXIT_PROTECT: 0
[17127.319573] [ELKEID] ANTI_ROOTKIT_CHECK: 1
拿一个进程做实验,下面的2738号进程
[[email protected] log]# systemctl status wazuh-manager.service
● wazuh-manager.service - Wazuh manager
Loaded: loaded (/etc/systemd/system/wazuh-manager.service; enabled; vendor preset: disabled)
Active: active (running) since Thu 2021-08-12 11:37:26 CST; 4h 53min ago
Process: 976 ExecStart=/usr/bin/env ${DIRECTORY}/bin/ossec-control start (code=exited, status=0/SUCCESS)
Tasks: 106 (limit: 23371)
Memory: 1001.8M
CGroup: /system.slice/wazuh-manager.service
├─2738 /var/ossec/bin/ossec-authd
├─2759 /var/ossec/bin/wazuh-db
├─2778 /var/ossec/bin/ossec-execd
├─2785 /var/ossec/bin/ossec-analysisd
├─2801 /var/ossec/bin/ossec-syscheckd
├─2819 /var/ossec/bin/ossec-remoted
├─2828 /var/ossec/bin/ossec-logcollector
├─2844 /var/ossec/bin/ossec-monitord
└─2852 /var/ossec/bin/wazuh-modulesd
使用GDB
[[email protected] log]# gdb -p 2738 -ex "detach" -q -ex "q"
Attaching to process 2738
[New LWP 2754]
[New LWP 2755]
[New LWP 2756]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
0x00007f38761a629f in select () from /lib64/libc.so.6
Detaching from program: /var/ossec/bin/ossec-authd, process 2738
[Inferior 1 (process 2738) detached]
根据https://github.com/bytedance/Elkeid/blob/main/driver/README-zh_CN.md的说明,在/proc/hids_driver/1
下看结果
[[email protected] LKM]# cat /proc/hids_driver/1
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652755139880494745864-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652755139880494748408-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652755139880494757320-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652755139880494756696-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652755139880494812704-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652755139880494816856-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652755139880494817520-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652755139880494745864-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652755139880494757320-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652755139880494756696-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652755139880494816856-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652738139880494748408-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652738139880494812704-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
0101/usr/libexec/gdb219632050821963219636879gdblocalhost.localdomain14026531836402653183652738139880494817520-121963.gdb<20508.bash<20507.su<6879.bash<6523.sshd<6507.sshd<1061.sshd<1.systemd
呃,这个输出有点难阅读。它的打印格式是在driver/LKM/include/kprobe_print.h
里定义
PRINT_EVENT_DEFINE(ptrace, PE_PROTO(long request,
long owner_pid, void *addr, char *data_res, char *exe_path, char *pid_tree),
PE_ARGS(request,
owner_pid, addr, data_res, exe_path, pid_tree),
PE_STRUCT__entry(
__field(int, uid)
__field(long, request)
__field(long, owner_pid)
__field(long, addr)
__string(data_res, data_res)
__string(exe_path, exe_path)
__string(pid_tree, pid_tree)
__field(int, pid)
__field(int, ppid)
__field(int, pgid)
__field(int, tgid)
__field(int, sid)
__array(char, comm, TASK_COMM_LEN)
__string(nodename, current->nsproxy->uts_ns->name.nodename)
__field(unsigned int, sessionid)
__field(unsigned int, pid_inum)
__field(unsigned int, root_pid_inum)
),
PE_fast_assign(
__entry->uid = __get_current_uid();
__entry->request = request;
__entry->owner_pid = owner_pid;
__entry->addr = (long) addr;
__assign_str(data_res, data_res);
__assign_str(exe_path, exe_path);
__assign_str(pid_tree, pid_tree);
__entry->pid = current->pid;
__entry->ppid = current->real_parent->tgid;
__entry->pgid = __get_pgid();
__entry->sid = __get_sid();
__entry->tgid = current->tgid;
memcpy(__entry->comm, current->comm, TASK_COMM_LEN);
__assign_str(nodename, current->nsproxy->uts_ns->name.nodename);
__entry->sessionid = __get_sessionid();
__entry->pid_inum = __get_pid_ns_inum();
__entry->root_pid_inum = ROOT_PID_NS_INUM;
),
PE_printk(
"%d" RS "101" RS "%s" RS "%d" RS "%d" RS "%d" RS "%d" RS "%d" RS "%s" RS "%s" RS "%u" RS "%u" RS "%u" RS "%ld" RS "%ld" RS "%ld" RS "%s" RS "%s",
__entry->uid, __get_str(exe_path),__entry->pid, __entry->ppid,
__entry->pgid, __entry->tgid, __entry->sid,__entry->comm, __get_str(nodename),
__entry->sessionid, __entry->pid_inum, __entry->root_pid_inum, __entry->request, __entry->owner_pid,
__entry->addr, __get_str(data_res),__get_str(pid_tree))
);
按照这种格式,至少可以知道gdb
是21963对2738号进程进行PTRACE_POKEDATA
(2738前面的5)。
可以看到,字节跳动HIDS
是可以对ptrace
进行检测,也能够记录ptrace
进行的操作。
=========================================
文中和文末的小广广,渴望你手指的触碰!!!
请关注,转发,点“在看”,谢谢!!
如需要转载,请在公众号留言!!
暗号:556e7