English | 简体中文
Elkeid Driver is a one-of-a-kind Kernel Space HIDS agent designed for Cyber-Security.
Elkeid Driver hooks kernel functions via Kprobe, providing rich and accurate data collection capabilities, including kernel-level process execve probing, privilege escalation monitoring, network audition, and much more. The Driver treats Container-based monitoring as a first-class citizen as Host-based data collection by supporting Linux Namespace. Compare to User Space agents on the market, Elkeid provides more comprehensive information with massive performance improvement.
Elkeid has already been deployed massively for HIDS usage in world-class production environments. With its marvelous data collection ability, Elkeid also supports Sandbox, Honeypot, and Audition data requirements.
DO NOT insmod the ko in the production machines if you have not well tested it.
First you need install Linux Headers
# clone and build
git clone https://github.com/bytedance/Elkeid.git
cd Elkeid/driver/LKM/
make clean && make
< CentOS only: run build script instead >
sh ./centos_build_ko.sh
# load and test (should run as root)
insmod hids_driver.ko
dmesg | tail -n 20
test/rst -q
< "CTRL + C" to quit >
# unload
rmmod hids_driver
If all urls failed, please build elkeid kernel module yourself.
wget "http://lf26-elkeid.bytetos.com/obj/elkeid-download/ko/hids_driver_1.7.0.10_$(uname -r)_amd64.ko"
You can test the kernel module with LTP (better with KASAN turned on). Here's the LTP-test-case configuration file for your reference: LTP-test-case.
Distro | Version | x64 kernel | Suffix |
---|---|---|---|
Debian | 8,9,10 | 3.16~5.4.X | - |
Ubuntu | 14.04,16.04,18.04,20.04 | 3.12~5.4.X | generic |
CentOS | 6.X,7.X,8.X | 2.6.32.0~5.4.X | el6,el7,el8 |
Amazon | 2 | 4.9.X~4.14.X | amzn2 |
AlibabaCloudLinux | 3 | 4.19.X~5.10.X | al7,al8 |
EulerOS | V2.0 | 3.10.X | - |
- Yes
- Linux Kernel Version >= 2.6.32 && <= 6.3
Source | Nodename |
---|---|
Host | hostname |
Docker | container name |
K8s | pod name |
Hook | DataType | Note | Default |
---|---|---|---|
write | 1 | OFF | |
open | 2 | OFF | |
mprotect | 10 | only PROT_EXEC | OFF |
nanosleep | 35 | OFF | |
connect | 42 | ON | |
accept | 43 | OFF | |
bind | 49 | ON | |
execve | 59 | ON | |
process exit | 60 | OFF | |
kill | 62 | OFF | |
rename | 82 | ON | |
link | 86 | ON | |
ptrace | 101 | only PTRACE_POKETEXT or PTRACE_POKEDATA | ON |
setsid | 112 | ON | |
prctl | 157 | only PR_SET_NAME | ON |
mount | 165 | ON | |
tkill | 200 | OFF | |
exit_group | 231 | OFF | |
memfd_create | 356 | ON | |
dns query | 601 | ON | |
create_file | 602 | ON | |
load_module | 603 | ON | |
update_cred | 604 | only old uid ≠0 && new uid == 0 | ON |
unlink | 605 | OFF | |
rmdir | 606 | OFF | |
call_usermodehelper_exec | 607 | ON | |
file_write | 608 | OFF | |
file_read | 609 | OFF | |
usb_device_event | 610 | ON | |
privilege_escalation | 611 | ON | |
port-scan detection | 612 | tunable via module param: psad_switch | OFF |
Rootkit | DataType | Default |
---|---|---|
interrupt table hook | 703 | ON |
syscall table hook | 701 | ON |
proc file hook | 700 | ON |
hidden kernel module | 702 | ON |
Every hit of the hook-points above will generate a record, and the records are separated by deliminator '\x17'.
Each record contains several data items, which are separated by deliminator '\x1e'.
A record generally contains Common Data and Private Data, with the exception of Anti-rootkit, which does NOT have Common Data.
-------------------------------------------------------------------------------
|1 |2 |3 |4 |5 |6 |7 |8 |9 |10 |11 |12 |13 |
-------------------------------------------------------------------------------
|data_type|uid|exe|pid|ppid|pgid|tgid|sid|comm|nodename|sessionid|pns|root_pns|
-------------------------------------------------------------------------------
-----------
|14 |15 |
-----------
|file||buf|
-----------
---------------------
|14 |15 |16 |
---------------------
|flags|mode|filename|
---------------------
-----------------------------------------------------
|14 |15 |16 |17 |18 |
-----------------------------------------------------
|mprotect_prot|owner_pid|owner_file|vm_file|pid_tree|
-----------------------------------------------------
----------
|14 |15 |
----------
|sec|nsec|
----------
-----------------------------------
|14 |15 |16 |17 |18 |19 |
-----------------------------------
|sa_family|dip|dport|sip|sport|res|
-----------------------------------
-----------------------------------
|14 |15 |16 |17 |18 |19 |
-----------------------------------
|sa_family|dip|dport|sip|sport|res|
-----------------------------------
-------------------------
|14 |15 |16 |17 |
-------------------------
|sa_family|sip|sport|res|
-------------------------
-----------------------------------------------------------------------------------------------------
|14 |15 |16 |17 |18 |19 |20 |21 |22 |23 |24 |25 |26 |27 |28 |
-----------------------------------------------------------------------------------------------------
|argv|run_path|stdin|stdout|dip|dport|sip|sport|sa_family|pid_tree|tty|socket_pid|ssh|ld_preload|res|
-----------------------------------------------------------------------------------------------------
Note:
-
socket_exe/dip/dport/sip/sport/sa_family is collected from the process's fds
-
ssh/ld_preload is collected from the process's env
Only contains fields in Common Data
----------------
|14 |15 |
----------------
|target_pid|sig|
----------------
-------------------------
|14 |15 |16 |
-------------------------
|old_name|new_name|sb_id|
-------------------------
-------------------------
|14 |15 |16 |
-------------------------
|old_name|new_name|sb_id|
-------------------------
----------------------------------------------
|14 |15 |16 |17 |18 |
----------------------------------------------
|ptrace_request|target_pid|addr|data|pid_tree|
----------------------------------------------
Only contains fields in Common Data
_________________
|14 |15 |
-----------------
|option|new_name|
-----------------
_____________________________________
|14 |15 |16 |17 |18 |
-------------------------------------
|pid_tree|dev|file_path|fstype|flags|
-------------------------------------
----------------
|14 |15 |
----------------
|target_pid|sig|
----------------
Only contains fields in Common Data
______________
|14 |15 |
--------------
|fdname|flags|
--------------
--------------------------------------------------
|14 |15 |16 |17 |18 |19 |20 |21 |
--------------------------------------------------
|query|sa_family|dip|dport|sip|sport|opcode|rcode|
--------------------------------------------------
----------------------------------------------------------
|14 |15 |16 |17 |18 |19 |20 |21 |
----------------------------------------------------------
|file_path|dip|dport|sip|sport|sa_family|socket_pid|sb_id|
----------------------------------------------------------
---------------------------
|14 |15 |16 |
---------------------------
|ko_file|pid_tree|run_path|
---------------------------
----------------------
|14 |15 |16 |
----------------------
|pid_tree|old_uid|res|
----------------------
------
|14 |
------
|file|
------
------
|14 |
------
|file|
------
-------------------------
|1 |2 |3 |4 |
-------------------------
|data_type|exe|argv|wait|
-------------------------
------------
|14 |15 |
------------
|file|sb_id|
------------
Need to join the to-watch list through Diver Filter, see "About Driver Filter" section for details
------------
|14 |15 |
------------
|file|sb_id|
------------
Need to join the to-watch list through Diver Filter, see "About Driver Filter" section for details
-----------------------------------------
|14 |15 |16 |17 |
-----------------------------------------
|product_info|manufacturer|serial|action|
-----------------------------------------
action = 1 is USB_DEVICE_ADD
action = 2 is USB_DEVICE_REMOVE
------------------------------
|14 |15 |16 |17 |
------------------------------
|p_pid|pid_tree|p_cred|c_cred|
------------------------------
p_cred = uid|euid|suid|fsuid|gid|egid|sgid|fsgid
c_cred = uid|euid|suid|fsuid|gid|egid|sgid|fsgid
------------------------------------------
|1 |2 |3 |4 |5 |6 |7 |
------------------------------------------
|type|sa_family|sip|sport|dip|dport|flags|
------------------------------------------
-----------------------
|1 |2 |
-----------------------
|data_type|module_name|
-----------------------
--------------------------------------
|1 |2 |3 |
--------------------------------------
|data_type|module_name|syscall_number|
--------------------------------------
Hidden Kernel Module Data (702)
-----------------------
|1 |2 |
-----------------------
|data_type|module_name|
-----------------------
----------------------------------------
|1 |2 |3 |
----------------------------------------
|data_type|module_name|interrupt_number|
----------------------------------------
Elkeid driver supports allowlist to filter out unwanted data. We provide two types of allowlists, 'exe' allowlist and 'argv' allowlist. 'exe' allowlist acts on execve/create file/dns query/connect hooks, while 'argv' allowlist only acts on execve hook. For performance and stability concerns, both 'exe' and 'argv' allowlist only supports 64-elements-wide capacity.
allowlist driver is in: /dev/hids_driver_allowlist
Operations | Flag | Example |
---|---|---|
ADD_EXECVE_EXE_SHITELIST | Y(89) | echo Y/bin/ls > /dev/someone_allowlist |
DEL_EXECVE_EXE_SHITELIST | F(70) | echo F/bin/ls > /dev/someone_allowlist |
DEL_ALL_EXECVE_EXE_SHITELIST | w(119) | echo w/del_all > /dev/someone_allowlist |
EXECVE_EXE_CHECK | y(121) | echo y/bin/ls > /dev/someone_allowlist && dmesg |
ADD_EXECVE_ARGV_SHITELIST | m(109) | echo m/bin/ls -l > /dev/someone_allowlist |
DEL_EXECVE_ARGV_SHITELIST | J(74) | echo J/bin/ls -l > /dev/someone_allowlist |
DEL_ALL_EXECVE_ARGV_SHITELIST | u(117) | echo u/del_all > /dev/someone_allowlist |
EXECVE_ARGV_CHECK | z(122) | echo z/bin/ls -l > /dev/someone_allowlist && dmesg |
PRINT_ALL_ALLOWLIST | .(46) | echo ./print_all > /dev/someone_allowlist && dmesg |
ADD_WRITE_NOTIFI | W(87) | echo W/etc/passwd > /dev/someone_allowlist or echo W/etc/ssh/ > /dev/someone_allowlist support dir |
DEL_WRITE_NOTIFI | v(120) | echo v/etc/passwd > /dev/someone_allowlist |
ADD_READ_NOTIFI | R(82) | echo R/etc/passwd > /dev/someone_allowlist or echo R/etc/ssh/ > /dev/someone_allowlist support dir |
DEL_READ_NOTIFI | s(115) | echo s/etc/passwd > /dev/someone_allowlist |
DEL_ALL_NOTIFI | A(65) | echo A/del_all_file_notift > /dev/someone_allowlist |
Filter define is:
#define ADD_EXECVE_EXE_SHITELIST 89 /* Y */
#define DEL_EXECVE_EXE_SHITELIST 70 /* F */
#define DEL_ALL_EXECVE_EXE_SHITELIST 119 /* w */
#define EXECVE_EXE_CHECK 121 /* y */
#define PRINT_ALL_ALLOWLIST 46 /* . */
#define ADD_EXECVE_ARGV_SHITELIST 109 /* m */
#define DEL_EXECVE_ARGV_SHITELIST 74 /* J */
#define DEL_ALL_EXECVE_ARGV_SHITELIST 117 /* u */
#define EXECVE_ARGV_CHECK 122 /* z */
#define ADD_WRITE_NOTIFI 87 /* W */
#define DEL_WRITE_NOTIFI 120 /* v */
#define ADD_READ_NOTIFI 82 /* R */
#define DEL_READ_NOTIFI 115 /* s */
#define DEL_ALL_NOTIFI 65 /* A */
CPU | Intel(R) Xeon(R) Platinum 8260 CPU @ 2.40GHz 8 Core |
---|---|
RAM | 16GB |
OS/Kernel | Debian9 / Kernel Version 4.14 |
Testing Load:
syscall | ltp |
---|---|
connect | ./runltp -f syscalls -s connect -t 5m |
bind | ./runltp -f syscalls -s bind -t 5m |
execve | ./runltp -f syscalls -s execve -t 5m |
security_inode_create | ./runltp -f syscalls -s open -t 5m |
ptrace | ./runltp -f syscalls -s ptrace -t 5m |
hook function name | Average Delay(us) | TP99(us) | TP95(us) | TP90(us) |
---|---|---|---|---|
connect_syscall_handler | 0.7454 | 3.5017 | 1.904 | 1.43 |
connect_syscall_entry_handler | 0.0675 | 0.3 | 0.163 | 0.1149 |
udp_recvmsg_handler | 9.1290 | 68.7043 | 18.5357 | 15.9528 |
udp_recvmsg_entry_handler | 0.5882 | 7.5631 | 0.7811 | 0.3665 |
bind_handler | 2.2558 | 10.0525 | 8.1996 | 7.041 |
bind_entry_handler | 0.4704 | 1.0180 | 0.8234 | 0.6739 |
execve_entry_handler | 6.9262 | 12.2824 | 9.437 | 8.638 |
execve_handler | 15.2102 | 36.0903 | 25.9272 | 23.068 |
security_inode_create_pre_handler | 1.5523 | 7.9454 | 5.5806 | 3.1441 |
ptrace_pre_handler | 0.2039 | 0.4648 | 0.254 | 0.228 |
udp_recvmsg_handler
will work only if the port is equal 53 or 5353
Original Testing Data: Benchmark Data
You can use DKMS or Pre-packaged ko file
- install driver:
insmod hids_driver.ko
- remove driver: first you need kill userspace agent and
rmmod hids_driver.ko
-
Hook point init failed: do_init_module
Some old version of Ubuntu / CentOS kernels may show the dmesg : do_init_module register_kprobe failed, returned -2. -
Description of Elkeid's Crash caused by fput in low version Kernel
Elkeid kernel module are distributed under the GNU GPLv2 license.