#include #include #include #include #include #include #include #include #include #include #include #include #include "net.h" #include "json.h" #include "error.h" #include "output.h" #include "md5.h" #include "sha256.h" #include "base64.h" #include "proc.h" #include "agent_context.h" #include "proc_ledger.h" #include "time_common.h" #include "string_common.h" #include "proc_connector.h" extern unsigned long maxsize; static void handle_proc_connector_message(struct cn_msg *cn_message, agent_context_t *ctx) { struct proc_event *event; json_t json = {0}; pid_t process; struct proc_ledger_entry *entry; event = (struct proc_event *)cn_message->data; switch (event->what) { case PROC_EVENT_NONE: break; case PROC_EVENT_FORK: pid_t child = event->event_data.fork.child_pid; entry = proc_ledger_entry_create(child, ctx); if (entry) { proc_ledger_add(ctx->proc_ledger, entry); } json = handle_PROC_EVENT_FORK(event, ctx); break; case PROC_EVENT_EXEC: process = event->event_data.exec.process_pid; entry = proc_ledger_entry_create(process, ctx); if (entry) { proc_ledger_replace(ctx->proc_ledger, entry); json = handle_PROC_EVENT_EXEC(event, ctx); json_t environment = handle_PROC_EVENT_EXEC_environment(event, ctx); if (environment.data) { output(json_get(&environment)); json_free(&environment); } } break; case PROC_EVENT_EXIT: process = event->event_data.exit.process_pid; json = handle_PROC_EVENT_EXIT(event, ctx); proc_ledger_remove(ctx->proc_ledger, process); break; case PROC_EVENT_UID: // update ledger with new UIDs entry = proc_ledger_find(ctx->proc_ledger, event->event_data.id.process_pid); if (entry) { int old_ruid = entry->uid; int old_euid = entry->euid; entry->uid = event->event_data.id.r.ruid; entry->euid = event->event_data.id.e.euid; json = handle_PROC_EVENT_UID(event, ctx, old_ruid, old_euid); } else { json = handle_PROC_EVENT_UID(event, ctx, -1, -1); } break; case PROC_EVENT_GID: // update ledger with new GIDs entry = proc_ledger_find(ctx->proc_ledger, event->event_data.id.process_pid); if (entry) { int old_rgid = entry->gid; int old_egid = entry->egid; entry->gid = event->event_data.id.r.rgid; entry->egid = event->event_data.id.e.egid; json = handle_PROC_EVENT_GID(event, ctx, old_rgid, old_egid); } else { json = handle_PROC_EVENT_GID(event, ctx, -1, -1); } break; case PROC_EVENT_PTRACE: json = handle_PROC_EVENT_PTRACE(event, ctx); break; case PROC_EVENT_SID: json = handle_PROC_EVENT_SID(event, ctx); break; case PROC_EVENT_COMM: // update ledger with new comm char *old_comm = NULL; entry = proc_ledger_find(ctx->proc_ledger, event->event_data.comm.process_pid); if (entry) { old_comm = strdup(entry->comm); strncpy(entry->comm, event->event_data.comm.comm, sizeof(entry->comm) - 1); entry->comm[sizeof(entry->comm) - 1] = '\0'; } json = handle_PROC_EVENT_COMM(event, ctx, old_comm); if (old_comm) { free(old_comm); } break; case PROC_EVENT_COREDUMP: json = handle_PROC_EVENT_COREDUMP(event, ctx); break; default: json = handle_PROC_EVENT_UNKNOWN(event, ctx); break; } /* If we have data to output, deal with it. */ if (json.data) { output(json_get(&json)); json_free(&json); } } sock_t setup_proc_connector(void) { int err; sock_t proc_connector; struct sockaddr_nl nl_userland; enum proc_cn_mcast_op *mcop_msg; struct cn_msg *cn_message; char buf[1024] = {0}; struct nlmsghdr *nl_header; proc_connector = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); if (proc_connector == -1) { error("error creating proc_connector socket: %s", strerror(errno)); return -1; } nl_userland.nl_family = AF_NETLINK; nl_userland.nl_groups = CN_IDX_PROC; nl_userland.nl_pid = getpid(); err = bind(proc_connector, (struct sockaddr *)&nl_userland, sizeof(nl_userland)); if (err == -1) { error("error binding proc_connector socket: %s", strerror(errno)); return -1; } nl_header = (struct nlmsghdr *)buf; cn_message = (struct cn_msg *)NLMSG_DATA(nl_header); mcop_msg = (enum proc_cn_mcast_op *)&cn_message->data[0]; *mcop_msg = PROC_CN_MCAST_LISTEN; cn_message->id.idx = CN_IDX_PROC; cn_message->id.val = CN_VAL_PROC; cn_message->seq = 0; cn_message->ack = 0; cn_message->len = sizeof(enum proc_cn_mcast_op); nl_header->nlmsg_len = NLMSG_LENGTH(sizeof(struct cn_msg) + sizeof(enum proc_cn_mcast_op)); nl_header->nlmsg_type = NLMSG_DONE; nl_header->nlmsg_flags = 0; nl_header->nlmsg_seq = 0; nl_header->nlmsg_pid = getpid(); err = send(proc_connector, nl_header, nl_header->nlmsg_len, 0); if (err != nl_header->nlmsg_len) { error("send: %s", strerror(errno)); close(proc_connector); return -1; } return proc_connector; } void select_proc_connector(sock_t proc_connector, agent_context_t *ctx) { int recv_length; socklen_t nl_kernel_len; struct nlmsghdr *nlh; struct cn_msg *cn_message; struct sockaddr_nl nl_kernel; char buf[1024] = {0}; nl_kernel_len = sizeof(nl_kernel); recv_length = recvfrom(proc_connector, buf, sizeof(buf), 0, (struct sockaddr *)&nl_kernel, &nl_kernel_len); nlh = (struct nlmsghdr *)buf; if ((recv_length < 1) || (nl_kernel.nl_pid != 0)) { return; } while (NLMSG_OK(nlh, recv_length)) { cn_message = NLMSG_DATA(nlh); if ((nlh->nlmsg_type == NLMSG_NOOP) || (nlh->nlmsg_type == NLMSG_ERROR)) { continue; } if (nlh->nlmsg_type == NLMSG_OVERRUN) { break; } handle_proc_connector_message(cn_message, ctx); if (nlh->nlmsg_type == NLMSG_DONE) { break; } else { nlh = NLMSG_NEXT(nlh, recv_length); } } } static inline const char *safe_exe_path(struct proc_ledger_entry *entry) { if (entry && entry->exe[0]) { return entry->exe; } return "UNKNOWN"; } /* handle_PROC_EVENT_FORK() - Handle PROC_EVENT_FORK events. * * The following are available for this event: * - pid_t parent_pid * - pid_t parent_tgid * - pid_t child_pid * - pid_t child_tgid * * Args: * event - proc_event structure (linux/cn_proc.h) * ctx - agent context containing process/hash hedgers * * Returns: * json_t containing serialized JSON object describing this event. * * TODO: Deal with parent_pid and child_pid being the same somehow (see below) */ json_t handle_PROC_EVENT_FORK(struct proc_event *event, agent_context_t *ctx) { pid_t parent_pid = event->event_data.fork.parent_pid; pid_t parent_tgid = event->event_data.fork.parent_tgid; pid_t child_pid = event->event_data.fork.child_pid; pid_t child_tgid = event->event_data.fork.child_tgid; struct proc_ledger_entry *parent_entry = proc_ledger_find(ctx->proc_ledger, parent_pid); if (!parent_entry) { json_t empty = {0}; return empty; } struct proc_ledger_entry *child_entry = proc_ledger_entry_create(child_pid, ctx); if (child_entry && parent_entry) { memcpy(child_entry, parent_entry, sizeof(struct proc_ledger_entry)); child_entry->pid = child_pid; child_entry->tgid = child_tgid; snprintf(child_entry->comm, sizeof(child_entry->comm), "%s", parent_entry->comm); proc_ledger_add(ctx->proc_ledger, child_entry); } json_t buf = {0}; json_init(&buf); json_start_object(&buf); json_add_double(&buf, "timestamp", timestamp()); json_add_string(&buf, "hostname", ctx->hostname); json_add_string(&buf, "event_type", "fork"); json_add_int(&buf, "parent_pid", parent_pid); json_add_int(&buf, "parent_tgid", parent_tgid); json_add_int(&buf, "child_pid", child_pid); json_add_int(&buf, "child_tgid", child_tgid); json_add_string(&buf, "parent_exepath", parent_entry->exe); json_add_string(&buf, "parent_comm", parent_entry->comm); json_add_string(&buf, "cmdline", parent_entry->cmdline); bool deleted = endswith(parent_entry->exe, " (deleted)"); json_add_bool(&buf, "deleted", deleted); json_add_int(&buf, "uid", parent_entry->uid); json_add_int(&buf, "euid", parent_entry->euid); json_add_int(&buf, "gid", parent_entry->gid); json_add_int(&buf, "egid", parent_entry->egid); // TODO is parent_entry correct, or child_entry? struct hash_entry *hentry = hash_ledger_find(ctx->hash_ledger, parent_entry->exe); if (hentry) { if (hentry->md5[0]) { json_add_string(&buf, "md5", hentry->md5); } if (hentry->sha256[0]) { json_add_string(&buf, "sha256", hentry->sha256); } json_add_double(&buf, "entropy", hentry->entropy); } json_end_object(&buf); return buf; } /* handle_PROC_EVENT_EXEC() - Handle PROC_EVENT_EXEC events. * * The following are available for this event: * - pid_t process_pid * - pid_t process_tgid * * Args: * event - proc_event structure (linux/cn_proc.h) * ctx - agent context containing process/hash hedgers * * Returns: * json_t containing serialized JSON object describing this event. */ json_t handle_PROC_EVENT_EXEC(struct proc_event *event, agent_context_t *ctx) { struct proc_ledger_entry *entry = proc_ledger_find(ctx->proc_ledger, event->event_data.exec.process_pid); if (!entry) { json_t empty = {0}; return empty; } json_t buf = {0}; json_init(&buf); json_start_object(&buf); json_add_double(&buf, "timestamp", timestamp()); json_add_string(&buf, "hostname", ctx->hostname); json_add_string(&buf, "event_type", "exec"); json_add_int(&buf, "pid", entry->pid); json_add_int(&buf, "tgid", entry->tgid); json_add_int(&buf, "uid", entry->uid); json_add_int(&buf, "euid", entry->euid); json_add_int(&buf, "gid", entry->gid); json_add_int(&buf, "egid", entry->egid); json_add_string(&buf, "exepath", entry->exe); json_add_string(&buf, "cmdline", entry->cmdline); json_add_string_or_null(&buf, "cwd", entry->cwd); struct hash_entry *hentry = hash_ledger_find(ctx->hash_ledger, entry->exe); if (hentry) { json_add_string_or_null(&buf, "md5", hentry->md5); json_add_string_or_null(&buf, "sha256", hentry->sha256); json_add_double(&buf, "entropy", hentry->entropy); } json_end_object(&buf); return buf; } /* handle_PROC_EVENT_EXEC_environment() - Grab the environment from * PROC_EVENT_EXEC events. * * This is called after handle_PROC_EVENT_EXEC() to attempt to grab * the environment from /proc/X/environ for a newly-executed * process. This information may be useful from a forensics * standpoint, and possibly to detect malicious activity. * * Since an environment can contain anything, it is base64 encoded so * it doesn't break our JSON formatting. * * Args: * event - proc_event structure (linux/cn_proc.h) * ctx - agent context containing process/hash hedgers * * Returns: * json_t containing serialized JSON object describing this event. */ json_t handle_PROC_EVENT_EXEC_environment(struct proc_event *event, agent_context_t *ctx) { struct proc_ledger_entry *entry = proc_ledger_find(ctx->proc_ledger, event->event_data.exec.process_pid); if (!entry) { json_t empty = {0}; return empty; } pid_t pid = event->event_data.exec.process_pid; char *environment = proc_environ(pid); if (!environment) { json_t empty = {0}; return empty; } json_t buf = {0}; json_init(&buf); json_start_object(&buf); json_add_double(&buf, "timestamp", timestamp()); json_add_string(&buf, "hostname", ctx->hostname); json_add_string(&buf, "event_type", "environment"); json_add_int(&buf, "pid", entry->pid); json_add_int(&buf, "uid", entry->uid); json_add_int(&buf, "euid", entry->euid); json_add_int(&buf, "gid", entry->gid); json_add_int(&buf, "egid", entry->egid); json_add_string(&buf, "exepath", entry->exe); json_add_string(&buf, "environment", environment); json_end_object(&buf); free(environment); return buf; } /* handle_PROC_EVENT_EXIT() - Handle PROC_EVENT_EXIT events. * * The following are available for this event: * - pid_t process_pid * - pid_t process_tgid * - u32 exit_code * - u32 exit_signal * * Args: * event - proc_event structure (linux/cn_proc.h) * ctx - agent context containing process/hash hedgers * * Returns: * json_t containing serialized JSON object describing this event. */ json_t handle_PROC_EVENT_EXIT(struct proc_event *event, agent_context_t *ctx) { struct proc_ledger_entry *entry = proc_ledger_find(ctx->proc_ledger, event->event_data.exit.process_pid); if (!entry) { json_t empty = {0}; return empty; } json_t buf = {0}; json_init(&buf); json_start_object(&buf); json_add_double(&buf, "timestamp", timestamp()); json_add_string(&buf, "hostname", ctx->hostname); json_add_string(&buf, "event_type", "exit"); json_add_int(&buf, "pid", event->event_data.exit.process_pid); json_add_int(&buf, "tgid", event->event_data.exit.process_tgid); json_add_int(&buf, "exitcode", event->event_data.exit.exit_code); json_add_int(&buf, "signal", event->event_data.exit.exit_signal); json_add_string(&buf, "exepath", safe_exe_path(entry)); json_end_object(&buf); return buf; } /* handle_PROC_EVENT_UID() - Handle PROC_EVENT_UID events. * handle_PROC_EVENT_GID() - Handle PROC_EVENT_GID events. * * The following are availabie for this event: * - pid_t process_ppid * - pid_t process_tgid * - union { u32 ruid; u32 rgid } r * - union { u32 euid; u32 egid } e * * Args: * event - proc_event structure (linux/cn_proc.h) * ctx - agent context containing process/hash hedgers * old_ruid - old real uid * old_euid - old effective uid * * Returns: * json_t containing serialized JSON object describing this event * * Note: * The handle_PROC_EVENT_UID and handle_PROC_EVENT_GID functions * are nearly identical. If no record of the old ruid/euid of the * process exists, -1 will be displayed */ json_t handle_PROC_EVENT_UID(struct proc_event *event, agent_context_t *ctx, int old_ruid, int old_euid) { struct proc_ledger_entry *entry = proc_ledger_find(ctx->proc_ledger, event->event_data.id.process_pid); if (!entry) { json_t empty = {0}; return empty; } json_t buf = {0}; json_init(&buf); json_start_object(&buf); json_add_double(&buf, "timestamp", timestamp()); json_add_string(&buf, "hostname", ctx->hostname); json_add_string(&buf, "event_type", "uid"); json_add_int(&buf, "pid", entry->pid); json_add_int(&buf, "tgid", entry->tgid); json_add_int(&buf, "old_ruid", old_ruid); json_add_int(&buf, "new_ruid", event->event_data.id.r.ruid); json_add_int(&buf, "old_euid", old_euid); json_add_int(&buf, "new_euid", event->event_data.id.e.euid); json_add_string(&buf, "exepath", entry->exe); json_end_object(&buf); return buf; } json_t handle_PROC_EVENT_GID(struct proc_event *event, agent_context_t *ctx, int old_rgid, int old_egid) { struct proc_ledger_entry *entry = proc_ledger_find(ctx->proc_ledger, event->event_data.id.process_pid); if (!entry) { json_t empty = {0}; return empty; } json_t buf = {0}; json_init(&buf); json_start_object(&buf); json_add_double(&buf, "timestamp", timestamp()); json_add_string(&buf, "hostname", ctx->hostname); json_add_string(&buf, "event_type", "gid"); json_add_int(&buf, "pid", entry->pid); json_add_int(&buf, "tgid", entry->tgid); json_add_int(&buf, "old_rgid", old_rgid); json_add_int(&buf, "new_rgid", event->event_data.id.r.rgid); json_add_int(&buf, "old_egid", old_egid); json_add_int(&buf, "new_egid", event->event_data.id.e.egid); json_add_string(&buf, "exepath", entry->exe); json_end_object(&buf); return buf; } /* handle_PROC_EVENT_PTRACE() - Handle PROC_EVENT_PTRACE events. * * The following are availabie for this event: * - pid_t process_ppid * - pid_t process_tgid * - pid_t tracer_pid * - pid_t tracer_tgid * * Args: * event - proc_event structure (linux/cn_proc.h) * ctx - agent context containing process/hash hedgers * * Returns: * json_t containing serialized JSON object describing this event * * Note: * This appears to be called AFTER ptrace() calls, so obtaining the * path, hash, etc of the tracer process may fail sometimes. */ json_t handle_PROC_EVENT_PTRACE(struct proc_event *event, agent_context_t *ctx) { pid_t target_pid = event->event_data.ptrace.process_pid; pid_t tracer_pid = event->event_data.ptrace.tracer_pid; struct proc_ledger_entry *target_entry = proc_ledger_find(ctx->proc_ledger, target_pid); struct proc_ledger_entry *tracer_entry = proc_ledger_find(ctx->proc_ledger, tracer_pid); json_t buf = {0}; json_init(&buf); json_start_object(&buf); json_add_double(&buf, "timestamp", timestamp()); json_add_string(&buf, "hostname", ctx->hostname); json_add_string(&buf, "event_type", "ptrace"); json_add_int(&buf, "pid", target_pid); json_add_int(&buf, "tgid", event->event_data.ptrace.process_tgid); json_add_int(&buf, "tracer_pid", tracer_pid); json_add_int(&buf, "tracer_tgid", event->event_data.ptrace.tracer_tgid); if (tracer_entry && tracer_entry->exe[0]) { json_add_string(&buf, "tracer_exepath", tracer_entry->exe); } if (target_entry && target_entry->exe[0]) { json_add_string(&buf, "target_exepath", target_entry->exe); } json_end_object(&buf); return buf; } /* handle_PROC_EVENT_SID() - Handle PROC_EVENT_SID events. * * The following are availabie for this event: * - pid_t process_ppid * - pid_t process_tgid * * Args: * event - proc_event structure (linux/cn_proc.h) * ctx - agent context containing process/hash hedgers * * Returns: * json_t containing serialized JSON object describing this event * * Note: * This event occurs when the process calls setsid(). This can be * used to detect when processes daemonize/fork/detach from a terminal. * * TODO: add contextual process ancestry to the output */ json_t handle_PROC_EVENT_SID(struct proc_event *event, agent_context_t *ctx) { struct proc_ledger_entry *entry = proc_ledger_find(ctx->proc_ledger, event->event_data.sid.process_pid); json_t buf = {0}; json_init(&buf); json_start_object(&buf); json_add_double(&buf, "timestamp", timestamp()); json_add_string(&buf, "hostname", ctx->hostname); json_add_string(&buf, "event_type", "sid"); json_add_int(&buf, "pid", event->event_data.sid.process_pid); json_add_int(&buf, "tgid", event->event_data.sid.process_tgid); if (entry && entry->exe[0]) { json_add_string(&buf, "exepath", entry->exe); } json_end_object(&buf); return buf; } /* handle_PROC_EVENT_COMM() - Handle PROC_EVENT_COMM events. * * The following are availabie for this event: * - pid_t process_ppid * - pid_t process_tgid * - char comm[16] * * Args: * event - proc_event structure (linux/cn_proc.h) * ctx - agent context containing process/hash hedgers * old_comm - string containing old value of comm * * Returns: * json_t containing serialized JSON object describing this event * * Note: * This may be triggered for prctl(PR_SET_NAME, ...) and be * suitable for finding processes attempting to obfuscate * themselves (ex: malware changing its name to 'sshd') * * Apparently this can be triggered by threads setting their names * as well. * * TODO: show old comm if possible * TODO: alert for "sketchy" names like sshd, cron, http, [kthread], ... */ json_t handle_PROC_EVENT_COMM(struct proc_event *event, agent_context_t *ctx, const char *old_comm) { struct proc_ledger_entry *entry = proc_ledger_find(ctx->proc_ledger, event->event_data.comm.process_pid); json_t buf = {0}; json_init(&buf); json_start_object(&buf); json_add_double(&buf, "timestamp", timestamp()); json_add_string(&buf, "hostname", ctx->hostname); json_add_string(&buf, "event_type", "comm"); json_add_int(&buf, "pid", event->event_data.comm.process_pid); json_add_int(&buf, "tgid", event->event_data.comm.process_tgid); json_add_string_or_null(&buf, "exepath", entry->exe); json_add_string_or_null(&buf, "old_comm", old_comm); json_add_string(&buf, "new_comm", event->event_data.comm.comm); json_end_object(&buf); return buf; } /* handle_PROC_EVENT_COREDUMP() - Handle PROC_EVENT_COREDUMP events. * * The following are availabie for this event: * - pid_t process_ppid * - pid_t process_tgid * - pid_t parent_pid * - pid_t parent_tgid * * Args: * event - proc_event structure (linux/cn_proc.h) * ctx - agent context containing process/hash hedgers * * Returns: * json_t containing serialized JSON object describing this event */ json_t handle_PROC_EVENT_COREDUMP(struct proc_event *event, agent_context_t *ctx) { struct proc_ledger_entry *entry = proc_ledger_find(ctx->proc_ledger, event->event_data.exit.process_pid); json_t buf = {0}; json_init(&buf); json_start_object(&buf); json_add_double(&buf, "timestamp", timestamp()); json_add_string(&buf, "hostname", ctx->hostname); json_add_string(&buf, "event_type", "coredump"); json_add_int(&buf, "pid", event->event_data.coredump.process_pid); json_add_int(&buf, "tgid", event->event_data.coredump.process_tgid); json_add_int(&buf, "parent_pid", event->event_data.coredump.parent_pid); json_add_int(&buf, "parent_tgid", event->event_data.coredump.parent_tgid); json_add_string(&buf, "exefile", safe_exe_path(entry)); json_end_object(&buf); return buf; } /* handle_PROC_EVENT_UNKNOWN() - Handle unknown events. * * This is a generic placeholder for unknown/unhandled events. * * Args: * event - proc_event structure (linux/cn_proc.h) * ctx - agent context * * Returns: * char * containing serialized JSON object describing the event */ json_t handle_PROC_EVENT_UNKNOWN(struct proc_event *event, agent_context_t *ctx) { json_t buf = {0}; json_init(&buf); json_start_object(&buf); json_add_double(&buf, "timestamp", timestamp()); json_add_string(&buf, "hostname", ctx->hostname); json_add_string(&buf, "event_type", "unknown"); json_add_int(&buf, "event_code", event->what); json_end_object(&buf); return buf; }