diff --git a/plugins/anomalydetection/src/plugin.cpp b/plugins/anomalydetection/src/plugin.cpp index 4294014a..127bea68 100644 --- a/plugins/anomalydetection/src/plugin.cpp +++ b/plugins/anomalydetection/src/plugin.cpp @@ -266,6 +266,9 @@ bool anomalydetection::init(falcosecurity::init_input& in) m_exe_writable = m_thread_table.get_field(t.fields(), "exe_writable", st::SS_PLUGIN_ST_BOOL); m_exe_upper_layer = m_thread_table.get_field(t.fields(), "exe_upper_layer", st::SS_PLUGIN_ST_BOOL); m_exe_from_memfd = m_thread_table.get_field(t.fields(), "exe_from_memfd", st::SS_PLUGIN_ST_BOOL); + m_exe_ino = m_thread_table.get_field(t.fields(), "exe_ino", st::SS_PLUGIN_ST_UINT64); + m_exe_ino_ctime = m_thread_table.get_field(t.fields(), "exe_ino_ctime", st::SS_PLUGIN_ST_UINT64); + m_exe_ino_mtime = m_thread_table.get_field(t.fields(), "exe_ino_mtime", st::SS_PLUGIN_ST_UINT64); m_args_value = t.get_subtable_field(m_thread_table, m_args, "value", st::SS_PLUGIN_ST_STRING); m_env_value = t.get_subtable_field(m_thread_table, m_env, "value", st::SS_PLUGIN_ST_STRING); m_vtid = m_thread_table.get_field(t.fields(), "vtid", st::SS_PLUGIN_ST_INT64); @@ -281,7 +284,7 @@ bool anomalydetection::init(falcosecurity::init_input& in) // m_loginuser = m_thread_table.get_field(t.fields(), "loginuser", TBD); // m_group = m_thread_table.get_field(t.fields(), "group", TBD); - /* fd or fs related */ + /* fd related */ // m_fd_type_value = t.get_subtable_field(m_thread_table, m_fds, "type", st::SS_PLUGIN_ST_UINT32); // todo fix, likely type issue given its of type scap_fd_type m_fd_openflags_value = t.get_subtable_field(m_thread_table, m_fds, "open_flags", st::SS_PLUGIN_ST_UINT32); // m_fd_sockinfo_value = t.get_subtable_field(m_thread_table, m_fds, "sock_info", st::SS_PLUGIN_ST_UINT32); // todo fix, likely type issue given its of type sinsp_sockinfo @@ -293,7 +296,7 @@ bool anomalydetection::init(falcosecurity::init_input& in) m_fd_mount_id_value = t.get_subtable_field(m_thread_table, m_fds, "mount_id", st::SS_PLUGIN_ST_UINT32); m_fd_ino_value = t.get_subtable_field(m_thread_table, m_fds, "ino", st::SS_PLUGIN_ST_UINT64); m_fd_pid_value = t.get_subtable_field(m_thread_table, m_fds, "pid", st::SS_PLUGIN_ST_INT64); - m_fd_fd_value = t.get_subtable_field(m_thread_table, m_fds, "fd", st::SS_PLUGIN_ST_INT64); + // m_fd_fd_value = t.get_subtable_field(m_thread_table, m_fds, "fd", st::SS_PLUGIN_ST_INT64); /* container related */ m_container_id = m_thread_table.get_field(t.fields(), "container_id", st::SS_PLUGIN_ST_STRING); @@ -423,6 +426,7 @@ bool anomalydetection::extract_filterchecks_concat_profile(const falcosecurity:: uint64_t tuint64 = UINT64_MAX; uint32_t tuint32 = UINT32_MAX; int64_t tint64 = -1; + bool tbool; int64_t ptid = -1; switch(field.id) { @@ -490,6 +494,34 @@ bool anomalydetection::extract_filterchecks_concat_profile(const falcosecurity:: }); break; } + case plugin_sinsp_filterchecks::TYPE_CMDNARGS: + { + const char* arg = nullptr; + size_t c = 0; + auto args_table = m_thread_table.get_subtable(tr, m_args, thread_entry, st::SS_PLUGIN_ST_INT64); + args_table.iterate_entries(tr, [this, &c](const falcosecurity::table_entry& e) + { + c++; + return true; + }); + tstr = std::to_string(c); + break; + } + case plugin_sinsp_filterchecks::TYPE_CMDLENARGS: + { + const char* arg = nullptr; + size_t c = 0; + auto args_table = m_thread_table.get_subtable(tr, m_args, thread_entry, st::SS_PLUGIN_ST_INT64); + args_table.iterate_entries(tr, [this, &tr, &arg, &c](const falcosecurity::table_entry& e) + { + arg = nullptr; + m_args_value.read_value(tr, e, arg); + c+=std::strlen(arg); + return true; + }); + tstr = std::to_string(c); + break; + } case plugin_sinsp_filterchecks::TYPE_CMDLINE: { m_comm.read_value(tr, thread_entry, tstr); @@ -511,6 +543,91 @@ bool anomalydetection::extract_filterchecks_concat_profile(const falcosecurity:: }); break; } + case plugin_sinsp_filterchecks::TYPE_PCMDLINE: + { + m_ptid.read_value(tr, thread_entry, ptid); + auto lineage = m_thread_table.get_entry(tr, ptid); + m_comm.read_value(tr, lineage, tstr); + const char* arg = nullptr; + auto args_table = m_thread_table.get_subtable(tr, m_args, lineage, st::SS_PLUGIN_ST_INT64); + args_table.iterate_entries(tr, [this, &tr, &arg, &tstr](const falcosecurity::table_entry& e) + { + arg = nullptr; + m_args_value.read_value(tr, e, arg); + if (!tstr.empty()) + { + tstr += " "; + } + if (arg) + { + tstr += arg; + } + return true; + }); + break; + } + case plugin_sinsp_filterchecks::TYPE_ACMDLINE: + { + if(field.argid < 1) + { + m_comm.read_value(tr, thread_entry, tstr); + const char* arg = nullptr; + auto args_table = m_thread_table.get_subtable(tr, m_args, thread_entry, st::SS_PLUGIN_ST_INT64); + args_table.iterate_entries(tr, [this, &tr, &arg, &tstr](const falcosecurity::table_entry& e) + { + arg = nullptr; + m_args_value.read_value(tr, e, arg); + if (!tstr.empty()) + { + tstr += " "; + } + if (arg) + { + tstr += arg; + } + return true; + }); + break; + } + m_ptid.read_value(tr, thread_entry, ptid); + for(uint32_t j = 0; j < field.argid; j++) + { + try + { + auto lineage = m_thread_table.get_entry(tr, ptid); + if(j == (field.argid - 1)) + { + m_comm.read_value(tr, lineage, tstr); + const char* arg = nullptr; + auto args_table = m_thread_table.get_subtable(tr, m_args, lineage, st::SS_PLUGIN_ST_INT64); + args_table.iterate_entries(tr, [this, &tr, &arg, &tstr](const falcosecurity::table_entry& e) + { + arg = nullptr; + m_args_value.read_value(tr, e, arg); + if (!tstr.empty()) + { + tstr += " "; + } + if (arg) + { + tstr += arg; + } + return true; + }); + break; + } + if(ptid == 1) + { + break; + } + m_ptid.read_value(tr, lineage, ptid); + } + catch(const std::exception& e) + { + } + } + break; + } case plugin_sinsp_filterchecks::TYPE_EXELINE: { m_exe.read_value(tr, thread_entry, tstr); @@ -679,7 +796,6 @@ bool anomalydetection::extract_filterchecks_concat_profile(const falcosecurity:: m_sid.read_value(tr, thread_entry, tint64); tstr = std::to_string(tint64); break; - // todo better unit tests and double check the parent lineage traversal fields in general case plugin_sinsp_filterchecks::TYPE_SNAME: { int64_t sid; @@ -708,6 +824,62 @@ bool anomalydetection::extract_filterchecks_concat_profile(const falcosecurity:: m_comm.read_value(tr, *leader, tstr); break; } + case plugin_sinsp_filterchecks::TYPE_SID_EXE: + { + int64_t sid; + m_sid.read_value(tr, thread_entry, sid); + m_ptid.read_value(tr, thread_entry, ptid); + falcosecurity::table_entry last_entry(nullptr, nullptr, nullptr); + falcosecurity::table_entry* leader = &thread_entry; + for(uint32_t j = 0; j < 9; j++) + { + try + { + auto lineage = m_thread_table.get_entry(tr, ptid); + m_sid.read_value(tr, lineage, tint64); + if(sid != tint64) + { + break; + } + m_ptid.read_value(tr, lineage, ptid); + last_entry = std::move(lineage); + leader = &last_entry; + } + catch(const std::exception& e) + { + } + } + m_exe.read_value(tr, *leader, tstr); + break; + } + case plugin_sinsp_filterchecks::TYPE_SID_EXEPATH: + { + int64_t sid; + m_sid.read_value(tr, thread_entry, sid); + m_ptid.read_value(tr, thread_entry, ptid); + falcosecurity::table_entry last_entry(nullptr, nullptr, nullptr); + falcosecurity::table_entry* leader = &thread_entry; + for(uint32_t j = 0; j < 9; j++) + { + try + { + auto lineage = m_thread_table.get_entry(tr, ptid); + m_sid.read_value(tr, lineage, tint64); + if(sid != tint64) + { + break; + } + m_ptid.read_value(tr, lineage, ptid); + last_entry = std::move(lineage); + leader = &last_entry; + } + catch(const std::exception& e) + { + } + } + m_exepath.read_value(tr, *leader, tstr); + break; + } case plugin_sinsp_filterchecks::TYPE_VPGID: m_vpgid.read_value(tr, thread_entry, tint64); tstr = std::to_string(tint64); @@ -740,9 +912,165 @@ bool anomalydetection::extract_filterchecks_concat_profile(const falcosecurity:: m_comm.read_value(tr, *leader, tstr); break; } + case plugin_sinsp_filterchecks::TYPE_VPGID_EXE: + { + int64_t vpgid; + m_vpgid.read_value(tr, thread_entry, vpgid); + m_ptid.read_value(tr, thread_entry, ptid); + falcosecurity::table_entry last_entry(nullptr, nullptr, nullptr); + falcosecurity::table_entry* leader = &thread_entry; + for(uint32_t j = 0; j < 5; j++) + { + try + { + auto lineage = m_thread_table.get_entry(tr, ptid); + m_vpgid.read_value(tr, lineage, tint64); + if(vpgid != tint64) + { + break; + } + m_ptid.read_value(tr, lineage, ptid); + last_entry = std::move(lineage); + leader = &last_entry; + } + catch(const std::exception& e) + { + } + } + m_exe.read_value(tr, *leader, tstr); + break; + } + case plugin_sinsp_filterchecks::TYPE_VPGID_EXEPATH: + { + int64_t vpgid; + m_vpgid.read_value(tr, thread_entry, vpgid); + m_ptid.read_value(tr, thread_entry, ptid); + falcosecurity::table_entry last_entry(nullptr, nullptr, nullptr); + falcosecurity::table_entry* leader = &thread_entry; + for(uint32_t j = 0; j < 5; j++) + { + try + { + auto lineage = m_thread_table.get_entry(tr, ptid); + m_vpgid.read_value(tr, lineage, tint64); + if(vpgid != tint64) + { + break; + } + m_ptid.read_value(tr, lineage, ptid); + last_entry = std::move(lineage); + leader = &last_entry; + } + catch(const std::exception& e) + { + } + } + m_exepath.read_value(tr, *leader, tstr); + break; + } + case plugin_sinsp_filterchecks::TYPE_ENV: + { + const char* env = nullptr; + auto env_table = m_thread_table.get_subtable(tr, m_env, thread_entry, st::SS_PLUGIN_ST_INT64); + auto argname = field.argname; + if(!argname.empty()) + { + size_t nlen = argname.length(); + env_table.iterate_entries(tr, [this, &tr, &env, &tstr, &nlen, &argname](const falcosecurity::table_entry& e) + { + env = nullptr; + std::string env_var; + m_env_value.read_value(tr, e, env); + if (env != nullptr) + { + env_var = std::string(env); + } + if((env_var.length() > (nlen + 1)) && (env_var[nlen] == '=') && + !env_var.compare(0, nlen, argname)) + { + size_t first = env_var.find_first_not_of(' ', nlen + 1); + size_t last = env_var.find_last_not_of(' '); + tstr = env_var.substr(first, last - first + 1); + } + return true; + }); + } else + { + env_table.iterate_entries(tr, [this, &tr, &env, &tstr](const falcosecurity::table_entry& e) + { + env = nullptr; + m_env_value.read_value(tr, e, env); + if (!tstr.empty()) + { + tstr += " "; + } + if (env) + { + tstr += env; + } + return true; + }); + } + break; + } + case plugin_sinsp_filterchecks::TYPE_IS_EXE_WRITABLE: + { + m_exe_writable.read_value(tr, thread_entry, tbool); + tstr = std::to_string(tbool); + break; + } + case plugin_sinsp_filterchecks::TYPE_IS_EXE_UPPER_LAYER: + { + m_exe_upper_layer.read_value(tr, thread_entry, tbool); + tstr = std::to_string(tbool); + break; + } + case plugin_sinsp_filterchecks::TYPE_IS_EXE_FROM_MEMFD: + { + m_exe_from_memfd.read_value(tr, thread_entry, tbool); + tstr = std::to_string(tbool); + break; + } + case plugin_sinsp_filterchecks::TYPE_EXE_INO: + { + m_exe_ino.read_value(tr, thread_entry, tuint64); + tstr = std::to_string(tuint64); + break; + } + case plugin_sinsp_filterchecks::TYPE_EXE_INO_CTIME: + { + m_exe_ino_ctime.read_value(tr, thread_entry, tuint64); + tstr = std::to_string(tuint64); + break; + } + case plugin_sinsp_filterchecks::TYPE_EXE_INO_MTIME: + { + m_exe_ino_mtime.read_value(tr, thread_entry, tuint64); + tstr = std::to_string(tuint64); + break; + } + case plugin_sinsp_filterchecks::TYPE_IS_SID_LEADER: + { + uint64_t vpid; + m_sid.read_value(tr, thread_entry, tint64); + m_vpid.read_value(tr, thread_entry, vpid); + tbool = tint64 == vpid; + tstr = std::to_string(tbool); + break; + } + case plugin_sinsp_filterchecks::TYPE_IS_VPGID_LEADER: + { + uint64_t vpid; + m_vpgid.read_value(tr, thread_entry, tint64); + m_vpid.read_value(tr, thread_entry, vpid); + tbool = tint64 == vpid; + tstr = std::to_string(tbool); + break; + } + // - // fd or fs related + // fd related // // todo implement fallbacks from null fd table entry aka extract from evt args diff --git a/plugins/anomalydetection/src/plugin.h b/plugins/anomalydetection/src/plugin.h index 1e52b623..f7a66e63 100644 --- a/plugins/anomalydetection/src/plugin.h +++ b/plugins/anomalydetection/src/plugin.h @@ -159,6 +159,12 @@ class anomalydetection falcosecurity::table_field m_exe_writable; falcosecurity::table_field m_exe_upper_layer; ///< True if the executable file belongs to upper layer in overlayfs falcosecurity::table_field m_exe_from_memfd; ///< True if the executable is stored in fileless memory referenced by memfd + falcosecurity::table_field m_exe_ino; + falcosecurity::table_field m_exe_ino_ctime; + falcosecurity::table_field m_exe_ino_mtime; + // falcosecurity::table_field m_cap_permitted; // todo fix + // falcosecurity::table_field m_cap_inheritable; // todo fix + // falcosecurity::table_field m_cap_effective; // todo fix falcosecurity::table_field m_args_value; ///< Value entry to command line arguments (e.g. "-d1") from the args array falcosecurity::table_field m_env_value; ///< Value entry falcosecurity::table_field m_group; ///< group infos @@ -175,10 +181,10 @@ class anomalydetection falcosecurity::table_field m_loginuid; ///< auid falcosecurity::table_field m_loginuser; ///< loginuser infos (auid) - /* fd or fs related */ - falcosecurity::table_field m_fd_type_value; // todo fix + /* fd related */ + // falcosecurity::table_field m_fd_type_value; // todo fix falcosecurity::table_field m_fd_openflags_value; - falcosecurity::table_field m_fd_sockinfo_value; // todo fix + // falcosecurity::table_field m_fd_sockinfo_value; // todo fix falcosecurity::table_field m_fd_name_value; falcosecurity::table_field m_fd_nameraw_value; falcosecurity::table_field m_fd_oldname_value; @@ -187,7 +193,7 @@ class anomalydetection falcosecurity::table_field m_fd_mount_id_value; falcosecurity::table_field m_fd_ino_value; falcosecurity::table_field m_fd_pid_value; - falcosecurity::table_field m_fd_fd_value; + // falcosecurity::table_field m_fd_fd_value; // todo fix /* container related */ falcosecurity::table_field m_container_id; ///< heuristic-based container id diff --git a/plugins/anomalydetection/src/plugin_utils.cpp b/plugins/anomalydetection/src/plugin_utils.cpp index b1495e20..e7e0654b 100644 --- a/plugins/anomalydetection/src/plugin_utils.cpp +++ b/plugins/anomalydetection/src/plugin_utils.cpp @@ -36,10 +36,10 @@ static const filtercheck_field_info sinsp_filter_check_fields[] = {PT_UINT64, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_DEC, "proc.cmdnargs", "Number of Command Line args", "The number of command line args (proc.args)."}, {PT_UINT64, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_DEC, "proc.cmdlenargs", "Total Count of Characters in Command Line args", "The total count of characters / length of the command line args (proc.args) combined excluding whitespaces between args."}, {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "proc.exeline", "Executable Command Line", "The full command line, with exe as first argument (proc.exe + proc.args) when starting the process generating the event."}, - {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.env", "Environment", "The environment variables of the process generating the event as concatenated string 'ENV_NAME=value ENV_NAME1=value1'. Can also be used to extract the value of a known env variable, e.g. proc.env[ENV_NAME]."}, + {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_ARG_ALLOWED, PF_NA, "proc.env", "Environment", "The environment variables of the process generating the event as concatenated string 'ENV_NAME=value ENV_NAME1=value1'. Can also be used to extract the value of a known env variable, e.g. proc.env[ENV_NAME]."}, {PT_CHARBUF, EPF_ARG_ALLOWED | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_NA, "proc.aenv", "Ancestor Environment", "[EXPERIMENTAL] This field can be used in three flavors: (1) as a filter checking all parents, e.g. 'proc.aenv contains xyz', which is similar to the familiar 'proc.aname contains xyz' approach, (2) checking the `proc.env` of a specified level of the parent, e.g. 'proc.aenv[2]', which is similar to the familiar 'proc.aname[2]' approach, or (3) checking the first matched value of a known ENV_NAME in the parent lineage, such as 'proc.aenv[ENV_NAME]' (across a max of 20 ancestor levels). This field may be deprecated or undergo breaking changes in future releases. Please use it with caution."}, {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "proc.cwd", "Current Working Directory", "The current working directory of the event."}, - {PT_INT64, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_ID, "proc.loginshellid", "Login Shell ID", "The pid of the oldest shell among the ancestors of the current process, if there is one. This field can be used to separate different user sessions."}, + {PT_INT64, EPF_NONE, PF_ID, "proc.loginshellid", "Login Shell ID", "The pid of the oldest shell among the ancestors of the current process, if there is one. This field can be used to separate different user sessions."}, {PT_UINT32, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_ID, "proc.tty", "Process TTY", "The controlling terminal of the process. 0 for processes without a terminal."}, {PT_INT64, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_ID, "proc.pid", "Process ID", "The id of the process generating the event."}, {PT_INT64, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_ID, "proc.ppid", "Parent Process ID", "The pid of the parent of the process generating the event."}, @@ -69,9 +69,9 @@ static const filtercheck_field_info sinsp_filter_check_fields[] = {PT_ABSTIME, EPF_NONE, PF_DEC, "proc.exe_ino.ctime_duration_proc_start", "Number of nanoseconds between ctime exe file and proc clone ts", "Number of nanoseconds between modifying status of executable image and spawning a new process using the changed executable image."}, {PT_ABSTIME, EPF_NONE, PF_DEC, "proc.exe_ino.ctime_duration_pidns_start", "Number of nanoseconds between pidns start ts and ctime exe file", "Number of nanoseconds between PID namespace start ts and ctime exe file if PID namespace start predates ctime."}, {PT_UINT64, EPF_NONE, PF_DEC, "proc.pidns_init_start_ts", "Start ts of pid namespace", "Start of PID namespace (container or non container pid namespace) as epoch timestamp in nanoseconds."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "thread.cap_permitted", "Permitted capabilities", "The permitted capabilities set"}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "thread.cap_inheritable", "Inheritable capabilities", "The inheritable capabilities set"}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "thread.cap_effective", "Effective capabilities", "The effective capabilities set"}, + {PT_CHARBUF, EPF_NONE, PF_NA, "thread.cap_permitted", "Permitted capabilities", "The permitted capabilities set"}, + {PT_CHARBUF, EPF_NONE, PF_NA, "thread.cap_inheritable", "Inheritable capabilities", "The inheritable capabilities set"}, + {PT_CHARBUF, EPF_NONE, PF_NA, "thread.cap_effective", "Effective capabilities", "The effective capabilities set"}, {PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_healthcheck", "Process Is Container Healthcheck", "'true' if this process is running as a part of the container's health check."}, {PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_liveness_probe", "Process Is Container Liveness", "'true' if this process is running as a part of the container's liveness probe."}, {PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_readiness_probe", "Process Is Container Readiness", "'true' if this process is running as a part of the container's readiness probe."}, @@ -168,12 +168,12 @@ static const filtercheck_field_info sinsp_filter_check_fields[] = {PT_INT64, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_DEC, "fd.ino", "FD Inode Number", "inode number of the referenced file"}, {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fd.nameraw", "FD Name Raw", "FD full name raw. Just like fd.name, but only used if fd is a file path. File path is kept raw with limited sanitization and without deriving the absolute path."}, {PT_CHARBUF, EPF_IS_LIST | EPF_ARG_ALLOWED | EPF_NO_RHS | EPF_NO_TRANSFORMER, PF_DEC, "fd.types", "FD Type", "List of FD types in used. Can be passed an fd number e.g. fd.types[0] to get the type of stdout as a single item list."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fs.path.name", "Path for Filesystem-related operation", "For any event type that deals with a filesystem path, the path the file syscall is operating on. This path is always fully resolved, prepending the thread cwd when needed."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fs.path.nameraw", "Raw path for Filesystem-related operation", "For any event type that deals with a filesystem path, the path the file syscall is operating on. This path is always the path provided to the syscall and may not be fully resolved."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fs.path.source", "Source path for Filesystem-related operation", "For any event type that deals with a filesystem path, and specifically for a source and target like mv, cp, etc, the source path the file syscall is operating on. This path is always fully resolved, prepending the thread cwd when needed."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fs.path.sourceraw", "Source path for Filesystem-related operation", "For any event type that deals with a filesystem path, and specifically for a source and target like mv, cp, etc, the source path the file syscall is operating on. This path is always the path provided to the syscall and may not be fully resolved."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fs.path.target", "Target path for Filesystem-related operation", "For any event type that deals with a filesystem path, and specifically for a target and target like mv, cp, etc, the target path the file syscall is operating on. This path is always fully resolved, prepending the thread cwd when needed."}, - {PT_CHARBUF, EPF_ANOMALY_PLUGIN | EPF_NONE, PF_NA, "fs.path.targetraw", "Target path for Filesystem-related operation", "For any event type that deals with a filesystem path, and specifically for a target and target like mv, cp, etc, the target path the file syscall is operating on. This path is always the path provided to the syscall and may not be fully resolved."}, + {PT_CHARBUF, EPF_NONE, PF_NA, "fs.path.name", "Path for Filesystem-related operation", "For any event type that deals with a filesystem path, the path the file syscall is operating on. This path is always fully resolved, prepending the thread cwd when needed."}, + {PT_CHARBUF, EPF_NONE, PF_NA, "fs.path.nameraw", "Raw path for Filesystem-related operation", "For any event type that deals with a filesystem path, the path the file syscall is operating on. This path is always the path provided to the syscall and may not be fully resolved."}, + {PT_CHARBUF, EPF_NONE, PF_NA, "fs.path.source", "Source path for Filesystem-related operation", "For any event type that deals with a filesystem path, and specifically for a source and target like mv, cp, etc, the source path the file syscall is operating on. This path is always fully resolved, prepending the thread cwd when needed."}, + {PT_CHARBUF, EPF_NONE, PF_NA, "fs.path.sourceraw", "Source path for Filesystem-related operation", "For any event type that deals with a filesystem path, and specifically for a source and target like mv, cp, etc, the source path the file syscall is operating on. This path is always the path provided to the syscall and may not be fully resolved."}, + {PT_CHARBUF, EPF_NONE, PF_NA, "fs.path.target", "Target path for Filesystem-related operation", "For any event type that deals with a filesystem path, and specifically for a target and target like mv, cp, etc, the target path the file syscall is operating on. This path is always fully resolved, prepending the thread cwd when needed."}, + {PT_CHARBUF, EPF_NONE, PF_NA, "fs.path.targetraw", "Target path for Filesystem-related operation", "For any event type that deals with a filesystem path, and specifically for a target and target like mv, cp, etc, the target path the file syscall is operating on. This path is always the path provided to the syscall and may not be fully resolved."}, }; // Temporary workaround; not as robust as libsinsp/eventformatter; @@ -185,57 +185,67 @@ namespace plugin_anomalydetection::utils { const std::vector get_profile_fields(const std::string& behavior_profile) { - std::vector fields; - std::regex pattern(R"(%(\S+))"); - std::sregex_iterator iter(behavior_profile.begin(), behavior_profile.end(), pattern); - std::sregex_iterator end; + std::vector fields; + std::regex pattern(R"(%(\S+))"); + std::sregex_iterator iter(behavior_profile.begin(), behavior_profile.end(), pattern); + std::sregex_iterator end; - plugin_sinsp_filterchecks::check_type id; - std::string fieldname; - std::int32_t argid = 0; - std::string argname = ""; + plugin_sinsp_filterchecks::check_type id; + std::string fieldname; + std::int32_t argid = 0; + std::string argname = ""; - while (iter != end) - { - auto rawfield = iter->str().substr(1); - std::string fieldname = rawfield; - for (size_t i = 0; i < sizeof(sinsp_filter_check_fields) / sizeof(sinsp_filter_check_fields[0]); ++i) - { - id = static_cast(i); - if(id == plugin_sinsp_filterchecks::TYPE_APID || - id == plugin_sinsp_filterchecks::TYPE_ANAME || - id == plugin_sinsp_filterchecks::TYPE_AEXE || - id == plugin_sinsp_filterchecks::TYPE_AEXEPATH || - id == plugin_sinsp_filterchecks::TYPE_ACMDLINE) - { - size_t start_pos = rawfield.find('['); - size_t end_pos = rawfield.find(']'); - if (start_pos != std::string::npos && end_pos != std::string::npos) - { - fieldname = rawfield.substr(0, start_pos); - argid = std::stoi(rawfield.substr(start_pos + 1, end_pos - start_pos - 1)); - } - } - if (std::string(sinsp_filter_check_fields[i].m_name) == fieldname) - { + while (iter != end) + { + auto rawfield = iter->str().substr(1); + std::string fieldname = rawfield; + for (size_t i = 0; i < sizeof(sinsp_filter_check_fields) / sizeof(sinsp_filter_check_fields[0]); ++i) + { + id = static_cast(i); + if(id == plugin_sinsp_filterchecks::TYPE_ENV || + id == plugin_sinsp_filterchecks::TYPE_APID || + id == plugin_sinsp_filterchecks::TYPE_ANAME || + id == plugin_sinsp_filterchecks::TYPE_AEXE || + id == plugin_sinsp_filterchecks::TYPE_AEXEPATH || + id == plugin_sinsp_filterchecks::TYPE_ACMDLINE) + { + size_t start_pos = rawfield.find('['); + size_t end_pos = rawfield.find(']'); + if (start_pos != std::string::npos && end_pos != std::string::npos) + { + fieldname = rawfield.substr(0, start_pos); + std::string arg_str = rawfield.substr(start_pos + 1, end_pos - start_pos - 1); + if (!arg_str.empty()) + { + argname = rawfield.substr(start_pos + 1, end_pos - start_pos - 1); + if (std::all_of(argname.begin(), argname.end(), ::isdigit)) + { + argid = std::stoi(rawfield.substr(start_pos + 1, end_pos - start_pos - 1)); + argname.clear(); + } + } + } + } + if (std::string(sinsp_filter_check_fields[i].m_name) == fieldname) + { if ((sinsp_filter_check_fields[i].m_flags & EPF_ANOMALY_PLUGIN)) { fields.emplace_back(plugin_sinsp_filterchecks_field{ id, argid, argname - }); + }); } else { plugin_anomalydetection::utils::log_error("Remove the following unsupported behavior profile field: '" + fieldname + "' exiting..."); exit(1); } - } - argid = 0; - argname.clear(); - } - ++iter; - } - return fields; + } + argid = 0; + argname.clear(); + } + ++iter; + } + return fields; } } diff --git a/plugins/anomalydetection/test/include/test_helpers.h b/plugins/anomalydetection/test/include/test_helpers.h index ba187448..4a2b931e 100644 --- a/plugins/anomalydetection/test/include/test_helpers.h +++ b/plugins/anomalydetection/test/include/test_helpers.h @@ -16,11 +16,11 @@ limitations under the License. */ #define INIT_CONFIG "{\"count_min_sketch\":{\"enabled\":true,\"n_sketches\":3,\"gamma_eps\":[[0.001,0.0001],[0.001,0.0001],[0.001,0.0001]],\"behavior_profiles\":[\ -{\"fields\":\"%container.id %proc.name %proc.pname %proc.exepath %proc.pexepath %proc.tty %proc.vpid %proc.pvid]\",\ +{\"fields\":\"%container.id %proc.is_vpgid_leader %proc.is_sid_leader %proc.exe_ino %proc.exe_ino.ctime %proc.exe_ino.mtime %proc.is_exe_writable %proc.is_exe_upper_layer %proc.is_exe_from_memfd %proc.cmdnargs %proc.cmdlenargs %proc.env[HOME] %proc.exeline %proc.name %proc.pname %proc.exepath %proc.pexepath %proc.tty %proc.vpid %proc.pvid]\",\ \"event_codes\":[293,331]},\ -{\"fields\":\"%proc.pid %fd.num %fd.name %fd.directory %fd.filename %fd.dev %fd.ino %fd.nameraw %fs.path.name %fs.path.nameraw\",\ +{\"fields\":\"%proc.pid %fd.num %fd.name %fd.directory %fd.filename %fd.dev %fd.ino %fd.nameraw\",\ \"event_codes\":[3,307,327,23]},\ -{\"fields\":\"%container.id %proc.cmdline %proc.name %proc.aname[0] %proc.aname[1] %proc.aname[2] %proc.aname[3] %proc.aname[4] %proc.aname[5] %proc.aname[6] %proc.aname[7] %proc.pid %proc.apid[0] %proc.apid[1] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.apid[6] %proc.apid[7] %proc.exepath %proc.aexepath[0] %proc.aexepath[1] %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.aexepath[6] %proc.aexepath[7] %proc.vpgid %proc.vpgid.name %proc.sid %proc.sname\",\ +{\"fields\":\"%container.id %proc.cmdline %proc.acmdline %proc.pcmdline %proc.acmdline[1] %proc.name %proc.aname[0] %proc.aname[1] %proc.aname[2] %proc.aname[3] %proc.aname[4] %proc.aname[5] %proc.aname[6] %proc.aname[7] %proc.pid %proc.apid[0] %proc.apid[1] %proc.apid[2] %proc.apid[3] %proc.apid[4] %proc.apid[5] %proc.apid[6] %proc.apid[7] %proc.exepath %proc.aexepath[0] %proc.aexepath[1] %proc.aexepath[2] %proc.aexepath[3] %proc.aexepath[4] %proc.aexepath[5] %proc.aexepath[6] %proc.aexepath[7] %proc.vpgid %proc.vpgid.name %proc.vpgid.exe %proc.vpgid.exepath %proc.sid %proc.sname %proc.sid.exe %proc.sid.exepath\",\ \"event_codes\":[293,331]}]}}" #define ASSERT_PLUGIN_INITIALIZATION(p_o, p_l) \ diff --git a/plugins/anomalydetection/test/src/num/cms.ut.cpp b/plugins/anomalydetection/test/src/num/cms.ut.cpp index d8ec4871..18aeaab1 100644 --- a/plugins/anomalydetection/test/src/num/cms.ut.cpp +++ b/plugins/anomalydetection/test/src/num/cms.ut.cpp @@ -6,7 +6,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -91,7 +91,7 @@ TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields) add_event_advance_ts(increasing_ts(), parent_tid, PPME_SYSCALL_CLONE_20_X, 20, parent_pid, "bash", empty_bytebuf, parent_pid, parent_tid, null_pid, "", fdlimit, pgft_maj, pgft_min, (uint32_t)12088, (uint32_t)7208, (uint32_t)0, "init", scap_const_sized_buffer{cgroupsv.data(), cgroupsv.size()}, (uint32_t)(PPM_CL_CLONE_CHILD_CLEARTID | PPM_CL_CLONE_CHILD_SETTID | PPM_CL_CLONE_NEWPID | PPM_CL_CHILD_IN_PIDNS), (uint32_t)1000, (uint32_t)1000, parent_pid, parent_tid); add_event_advance_ts(increasing_ts(), child_tid, PPME_SYSCALL_CLONE_20_X, 20, (uint64_t)0, "bash", empty_bytebuf, child_pid, child_tid, parent_tid, "", fdlimit, pgft_maj, pgft_min, (uint32_t)12088, (uint32_t)3764, (uint32_t)0, "init", scap_const_sized_buffer{cgroupsv.data(), cgroupsv.size()}, (uint32_t)(PPM_CL_CLONE_CHILD_CLEARTID | PPM_CL_CLONE_CHILD_SETTID | PPM_CL_CLONE_NEWPID | PPM_CL_CHILD_IN_PIDNS), (uint32_t)1000, (uint32_t)1000, child_pid, child_tid); add_event_advance_ts(increasing_ts(), child_tid, PPME_SYSCALL_EXECVE_19_E, 1, "/bin/test-exe"); - evt = add_event_advance_ts(increasing_ts(), child_tid, PPME_SYSCALL_EXECVE_19_X, 27, (int64_t)0, "/bin/test-exe", scap_const_sized_buffer{argsv.data(), argsv.size()}, child_tid, child_pid, parent_tid, "", fdlimit, pgft_maj, pgft_min, (uint32_t)29612, (uint32_t)4, (uint32_t)0, "test-exe", scap_const_sized_buffer{cgroupsv.data(), cgroupsv.size()}, scap_const_sized_buffer{envv.data(), envv.size()}, (int32_t)34818, parent_pid, loginuid, (int32_t) PPM_EXE_WRITABLE, parent_pid, parent_pid, parent_pid, exe_ino, ctime, mtime, euid); + evt = add_event_advance_ts(increasing_ts(), child_tid, PPME_SYSCALL_EXECVE_19_X, 27, (int64_t)0, "/bin/test-exe", scap_const_sized_buffer{argsv.data(), argsv.size()}, child_tid, child_pid, parent_tid, "", fdlimit, pgft_maj, pgft_min, (uint32_t)29612, (uint32_t)4, (uint32_t)0, "test-exe", scap_const_sized_buffer{cgroupsv.data(), cgroupsv.size()}, scap_const_sized_buffer{envv.data(), envv.size()}, (int32_t)34818, child_tid, loginuid, (uint32_t) (PPM_EXE_WRITABLE | PPM_EXE_UPPER_LAYER), parent_pid, parent_pid, parent_pid, exe_ino, ctime, mtime, euid); ASSERT_EQ(get_field_as_string(evt, "proc.name"), "test-exe"); @@ -103,10 +103,10 @@ TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields) ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch[2]", pl_flist), "1"); ASSERT_TRUE(field_exists(evt, "anomaly.count_min_sketch.profile", pl_flist)); - ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile", pl_flist), "test-exeinit/bin/test-exe/sbin/init3481820"); - ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[0]", pl_flist), "test-exeinit/bin/test-exe/sbin/init3481820"); + ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile", pl_flist), "1024204816762626980000045881676262698000004577110229/home/user/bin/test-exe -c 'echo aGVsbG8K | base64 -d'test-exeinit/bin/test-exe/sbin/init3481820"); + ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[0]", pl_flist), "1024204816762626980000045881676262698000004577110229/home/user/bin/test-exe -c 'echo aGVsbG8K | base64 -d'test-exeinit/bin/test-exe/sbin/init3481820"); ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[1]", pl_flist), ""); - ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[2]", pl_flist), "test-exe -c 'echo aGVsbG8K | base64 -d'test-exetest-exeinit20201/bin/test-exe/bin/test-exe/sbin/init1init0init"); + ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[2]", pl_flist), "test-exe -c 'echo aGVsbG8K | base64 -d'test-exe -c 'echo aGVsbG8K | base64 -d'initinittest-exetest-exeinit20201/bin/test-exe/bin/test-exe/sbin/init20test-exe/bin/test-exe/bin/test-exe0init/sbin/init/sbin/init"); } TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields_proc_lineage) @@ -118,7 +118,7 @@ TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields_proc_l uint64_t pgid = 9999; uint32_t loginuid = UINT32_MAX - 1, euid = 2000U; scap_const_sized_buffer empty_bytebuf = {.buf = nullptr, .size = 0}; - std::vector args = {"-c", "'echo aGVsbG8K | base64 -d'"}; + std::vector args = {"-c", "cat test"}; std::string argsv = test_utils::to_null_delimited(args); /* Instantiate the default tree */ DEFAULT_TREE @@ -129,13 +129,14 @@ TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields_proc_l add_event_advance_ts(increasing_ts(), p5_t1_tid, PPME_SYSCALL_EXECVE_19_E, 1, "/usr/bin/p5_t1_exepath"); add_event_advance_ts(increasing_ts(), p5_t1_tid, PPME_SYSCALL_EXECVE_19_X, 27, (int64_t)0, "/usr/bin/p5_t1_exepath", scap_const_sized_buffer{argsv.data(), argsv.size()}, p5_t1_tid, p5_t1_tid, p5_t1_ptid, "", not_relevant_64, not_relevant_64, not_relevant_64, (uint32_t)29612, (uint32_t)4, (uint32_t)0, "p5_t1_comm", empty_bytebuf, empty_bytebuf, (int32_t)34818, pgid, loginuid, (int32_t) PPM_EXE_WRITABLE, not_relevant_64, not_relevant_64, not_relevant_64, not_relevant_64, not_relevant_64, not_relevant_64, euid); + args = {"-c", "'echo aGVsbG8K | base64 -d'"}; add_event_advance_ts(increasing_ts(), p6_t1_tid, PPME_SYSCALL_EXECVE_19_E, 1, "/usr/bin/p6_t1_exepath"); auto evt = add_event_advance_ts(increasing_ts(), p6_t1_tid, PPME_SYSCALL_EXECVE_19_X, 27, (int64_t)0, "/usr/bin/p6_t1_exepath", scap_const_sized_buffer{argsv.data(), argsv.size()}, p6_t1_tid, p6_t1_tid, p6_t1_ptid, "", not_relevant_64, not_relevant_64, not_relevant_64, (uint32_t)29612, (uint32_t)4, (uint32_t)0, "p6_t1_comm", empty_bytebuf, empty_bytebuf, (int32_t)34818, pgid, loginuid, (int32_t) PPM_EXE_WRITABLE, not_relevant_64, not_relevant_64, not_relevant_64, not_relevant_64, not_relevant_64, not_relevant_64, euid); - ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[2]", pl_flist), "p6_t1_comm -c 'echo aGVsbG8K | base64 -d'p6_t1_commp6_t1_commp5_t1_commp4_t1_commp3_t1_commp2_t1_comminit8787827672251/usr/bin/p6_t1_exepath/usr/bin/p6_t1_exepath/usr/bin/p5_t1_exepath/usr/bin/p4_t1_exepath/usr/bin/p3_t1_exepath/usr/bin/p2_t1_exepath/sbin/init9999p5_t1_comm0init"); + ASSERT_EQ(get_field_as_string(evt, "anomaly.count_min_sketch.profile[2]", pl_flist), "p6_t1_comm -c cat testp6_t1_comm -c cat testp5_t1_comm -c cat testp5_t1_comm -c cat testp6_t1_commp6_t1_commp5_t1_commp4_t1_commp3_t1_commp2_t1_comminit8787827672251/usr/bin/p6_t1_exepath/usr/bin/p6_t1_exepath/usr/bin/p5_t1_exepath/usr/bin/p4_t1_exepath/usr/bin/p3_t1_exepath/usr/bin/p2_t1_exepath/sbin/init9999p5_t1_comm/usr/bin/p5_t1_exepath/usr/bin/p5_t1_exepath0init/sbin/init/sbin/init"); } -TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields_fd_fs_files) +TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields_fd) { std::shared_ptr plugin_owner; filter_check_list pl_flist; @@ -166,24 +167,24 @@ TEST_F(sinsp_with_test_input, plugin_anomalydetection_filterchecks_fields_fd_net open_inspector(); sinsp_evt* evt = NULL; - sinsp_fdinfo* fdinfo = NULL; - int64_t client_fd = 8; + sinsp_fdinfo* fdinfo = NULL; + int64_t client_fd = 8; int64_t return_value = 0; add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_SOCKET_E, 3, (uint32_t) PPM_AF_INET, (uint32_t) SOCK_STREAM, (uint32_t) 0); - add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_SOCKET_X, 1, client_fd); + add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_SOCKET_X, 1, client_fd); - sockaddr_in client = test_utils::fill_sockaddr_in(DEFAULT_CLIENT_PORT, DEFAULT_IPV4_CLIENT_STRING); - sockaddr_in server = test_utils::fill_sockaddr_in(DEFAULT_SERVER_PORT, DEFAULT_IPV4_SERVER_STRING); + sockaddr_in client = test_utils::fill_sockaddr_in(DEFAULT_CLIENT_PORT, DEFAULT_IPV4_CLIENT_STRING); + sockaddr_in server = test_utils::fill_sockaddr_in(DEFAULT_SERVER_PORT, DEFAULT_IPV4_SERVER_STRING); - std::vector server_sockaddr = test_utils::pack_sockaddr(reinterpret_cast(&server)); - evt = add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_CONNECT_E, 2, client_fd, scap_const_sized_buffer{server_sockaddr.data(), server_sockaddr.size()}); - std::vector socktuple = test_utils::pack_socktuple(reinterpret_cast(&client), reinterpret_cast(&server)); - evt = add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_CONNECT_X, 3, return_value, scap_const_sized_buffer{socktuple.data(), socktuple.size()}, client_fd); + std::vector server_sockaddr = test_utils::pack_sockaddr(reinterpret_cast(&server)); + evt = add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_CONNECT_E, 2, client_fd, scap_const_sized_buffer{server_sockaddr.data(), server_sockaddr.size()}); + std::vector socktuple = test_utils::pack_socktuple(reinterpret_cast(&client), reinterpret_cast(&server)); + evt = add_event_advance_ts(increasing_ts(), 1, PPME_SOCKET_CONNECT_X, 3, return_value, scap_const_sized_buffer{socktuple.data(), socktuple.size()}, client_fd); - /* We are able to recover the fdinfo in the connect exit event even when interleaved */ - fdinfo = evt->get_fd_info(); - ASSERT_NE(fdinfo, nullptr); + /* We are able to recover the fdinfo in the connect exit event even when interleaved */ + fdinfo = evt->get_fd_info(); + ASSERT_NE(fdinfo, nullptr); ASSERT_EQ(get_field_as_string(evt, "fd.connected"), "true"); ASSERT_EQ(get_field_as_string(evt, "fd.name"), "172.40.111.222:54321->142.251.111.147:443");