First commit
This commit is contained in:
commit
0c10e12608
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
src/**.o
|
||||
src/ssh_trace
|
15
Makefile
Normal file
15
Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
GCC=gcc
|
||||
CL=clang-11
|
||||
CFLAGS=-Wall
|
||||
LIBS=-lbpf
|
||||
|
||||
all: ssh-trace.ebpf.o ssh-trace
|
||||
|
||||
ssh-trace.ebpf.o: src/ssh-trace.ebpf.c
|
||||
$(CL) -g -O2 -target bpf -c src/ssh-trace.ebpf.c -o src/ssh-trace.ebpf.o
|
||||
|
||||
ssh-trace: src/load_bpf.c
|
||||
$(GCC) $(CFLAGS) src/load_bpf.c -o ssh-trace $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -rf src/*.o && rm ssh-trace
|
54
README.md
Normal file
54
README.md
Normal file
@ -0,0 +1,54 @@
|
||||
# Introduction
|
||||
Nowadays, with the increase of numbers of servers in an infrastructure, it's important to trace all users activities for investigating when a suspicious activity has been detected. This project is borned for resolving that issue, which trace all user connected through SSH and the outcome is print to the stdout or to a file in rsyslog format.
|
||||
|
||||
The program detect all commands executed in the system from a user connected and the result is print into the terminal, the program has an advantage for detection any privilege escalations when the user switch to another one, and the program show to us the initial user connected with the username and the user who executed the command. The diagram below show us an example:
|
||||
|
||||

|
||||
|
||||
# Installation
|
||||
# Supported platforms
|
||||
The program is based on [eBPF](https://ebpf.io/). It's a technology for developping a program which is loaded into the Kernel for security, networking and tracing all event in the kernel. This program has been tested on these systems:
|
||||
|
||||
| System | Architecture | Version | Kernel version |
|
||||
|--------|--------------|---------|----------------|
|
||||
| Ubuntu | x64| 20.04| 5.15.0|
|
||||
| Debian | x64| 11| 5.10.0|
|
||||
|
||||
## Requirements
|
||||
The program is based on eBPF and developped in C language. You should install these packages if you want to generate the binary:
|
||||
|
||||
* bpftool
|
||||
* clang-11
|
||||
* libbpf-dev
|
||||
* gcc gcc-multilib
|
||||
|
||||
# Usage
|
||||
After you clone the project, you can move into it. The arboresence of the project is quite simple. You have the repository `src/` which contains all C sources and headers files and you have the Makefile for generating the binary with the command `make all`:
|
||||
|
||||
```
|
||||
$ git clone https://gitea.bucchino.org/gbucchino/ssh-trace
|
||||
$ cd ssh-trace
|
||||
$ make all
|
||||
```
|
||||
|
||||
That will generate the binary `ssh-trace` and you can execute it:
|
||||
|
||||
```
|
||||
$ sudo ./ssh-trace
|
||||
```
|
||||
|
||||
By default, the result is print into the stdout, but, you can export it to rsyslog file format with the parameter -f:
|
||||
|
||||
```
|
||||
$ sudo ./ssh-trace -f ssh-trace_`$(echo date '+%F')`.log
|
||||
$ cat ssh-trace*.log
|
||||
$ sudo ./ssh-trace -f logs-ssh
|
||||
Jan 03 12:21:33 ubuntu ssh-trace: <info> host=user@192.168.1.37;ppid=8516;pathname=/usr/bin/ls --color=auto -la /home/user;pid=9112
|
||||
Jan 03 12:21:35 ubuntu ssh-trace: <info> host=user@192.168.1.37;ppid=8516;pathname=/usr/sbin/ip address show;pid=9113
|
||||
Jan 03 12:21:37 ubuntu ssh-trace: <info> host=user@192.168.1.37;ppid=8516;pathname=/usr/bin/cat /etc/group;pid=9114
|
||||
```
|
||||
|
||||
If you want to read more about the project, you should go to my [blog](https://www.bucchino.org/projects/sshtrace/), I made an article regarding it. Enjoy the read :).
|
||||
|
||||
# References
|
||||
* https://developers.redhat.com/articles/2023/10/19/ebpf-application-development-beyond-basics#an_example_c_application_using_libbpf
|
BIN
example.png
Normal file
BIN
example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 102 KiB |
8
exec.sh
Executable file
8
exec.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/sh
|
||||
|
||||
|
||||
# clang-11 -Wall -g -O2 -target bpf -c src/ssh-trace.ebpf.c -o src/ssh-trace.ebpf.o && \
|
||||
# bpftool gen skeleton src/ssh-trace.ebpf.o name sshtrace > src/skel.ebpf.h
|
||||
# gcc -Wall src/load_bpf.c -o src/ssh-trace -lbpf && \
|
||||
make clean
|
||||
make all && sudo ./ssh-trace
|
28
src/common.h
Normal file
28
src/common.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef H_COMMON
|
||||
#define H_COMMON
|
||||
|
||||
#define FILENAME_SIZE 128
|
||||
#define COMM_LEN 24
|
||||
#define ARGS_CNT 20
|
||||
#define ARGS_LEN 128
|
||||
#define ARGS_TLEN (ARGS_CNT * ARGS_LEN)
|
||||
|
||||
|
||||
struct execve {
|
||||
pid_t pid;
|
||||
pid_t ppid;
|
||||
char filename[FILENAME_SIZE];
|
||||
};
|
||||
|
||||
struct event{
|
||||
pid_t pid;
|
||||
pid_t ppid;
|
||||
uid_t uid;
|
||||
char filename[FILENAME_SIZE];
|
||||
char comm[COMM_LEN];
|
||||
int argc;
|
||||
int tlen;
|
||||
char args[ARGS_TLEN];
|
||||
};
|
||||
|
||||
#endif
|
387
src/load_bpf.c
Normal file
387
src/load_bpf.c
Normal file
@ -0,0 +1,387 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <argp.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <utmp.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
// #include "skel.ebpf.h"
|
||||
#include "common.h"
|
||||
|
||||
static int fd_map_execve;
|
||||
static int fd_map_args;
|
||||
static struct arguments arguments;
|
||||
static FILE *f;
|
||||
static int running = 1;
|
||||
static char hostname[127];
|
||||
|
||||
struct arguments {
|
||||
char *filename;
|
||||
int to_output;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Functions for arguments
|
||||
* https://www.gnu.org/software/libc/manual/html_node/Argp-Example-3.html
|
||||
*/
|
||||
|
||||
static char doc[] = "SSH Trace usage:";
|
||||
static char args_doc[] = "ARG1 ARG2";
|
||||
|
||||
static error_t parse_opts(int key, char *arg, struct argp_state *state){
|
||||
struct arguments *arguments = state->input;
|
||||
switch(key){
|
||||
case 'f':
|
||||
arguments->filename = arg;
|
||||
arguments->to_output = 0;
|
||||
break;
|
||||
case 'o':
|
||||
arguments->to_output = 1;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
break;
|
||||
case ARGP_KEY_END:
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct arguments parse_args(int argc, char *argv[]){
|
||||
static const struct argp_option opts[] = {
|
||||
{"filename", 'f', "FILENAME", 0, "Save result to logs"},
|
||||
{"to-output", 'o', NULL, 0, "Print to output"},
|
||||
{NULL, 'h', NULL, OPTION_HIDDEN, "help"},
|
||||
{},
|
||||
};
|
||||
struct arguments arguments;
|
||||
arguments.filename = NULL;
|
||||
arguments.to_output = 1;
|
||||
static struct argp argp = {opts, parse_opts, args_doc, doc};
|
||||
|
||||
argp_parse(&argp, argc, argv, 0, 0, &arguments);
|
||||
return arguments;
|
||||
}
|
||||
|
||||
/* End functions arguments */
|
||||
|
||||
static void signalHandler(int signum){
|
||||
running = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function get the username from the uid
|
||||
*/
|
||||
static void get_username(uid_t uid, char *username){
|
||||
struct passwd *p;
|
||||
p = getpwuid(uid);
|
||||
if (p == NULL){
|
||||
printf("Failed to get the username from the UID\n");
|
||||
strncpy(username, "Unknown", 8);
|
||||
return;
|
||||
}
|
||||
strncpy(username, p->pw_name, 64);
|
||||
}
|
||||
|
||||
/*
|
||||
* With the pid, walk through the process tree and to find the sshd proc
|
||||
*/
|
||||
static pid_t walk_process_tree(const pid_t pid){
|
||||
char filename[64];
|
||||
FILE *fd;
|
||||
int ppid;
|
||||
char proc_name[128];
|
||||
|
||||
snprintf(filename, 64, "/proc/%d/stat", pid);
|
||||
|
||||
if ((fd = fopen(filename, "r")) == NULL){
|
||||
printf("Failed to open the file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 1866044 (sshd) S 1 1866044
|
||||
fscanf(fd, "%*d %s %*c %d", proc_name, &ppid);
|
||||
|
||||
if (strncmp(proc_name, "(sshd)", 128) == 0){
|
||||
fclose(fd);
|
||||
return ppid;
|
||||
}
|
||||
|
||||
// If parent processes
|
||||
if (ppid == 0 || ppid == 1){
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// printf("PID: %d; PPID: %d; Proc name: %s\n", pid, ppid, proc_name);
|
||||
|
||||
fclose(fd);
|
||||
return walk_process_tree(ppid);
|
||||
}
|
||||
|
||||
/*
|
||||
* We can find the user attached to the pid
|
||||
*/
|
||||
static struct utmp find_user_from_pid(pid_t pid){
|
||||
int fd;
|
||||
size_t len;
|
||||
struct utmp u = {0};
|
||||
|
||||
if ((fd = open("/var/run/utmp", O_RDONLY)) < 0){
|
||||
printf("Failed to open the file\n");
|
||||
return u;
|
||||
}
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
while ((len = read(fd, &u, sizeof(struct utmp))) > 0){
|
||||
if (u.ut_type == USER_PROCESS && u.ut_pid == pid)
|
||||
break;
|
||||
memset(&u, 0, sizeof(struct utmp));
|
||||
}
|
||||
close(fd);
|
||||
return u;
|
||||
}
|
||||
/*
|
||||
* Try to find the sshd forked in the map
|
||||
*/
|
||||
static struct execve find_sshd_daemon(pid_t pid){
|
||||
struct execve s_execve = {0};
|
||||
// Get the execve struct
|
||||
int err = bpf_map_lookup_elem(fd_map_execve, &pid, &s_execve);
|
||||
if (err != 0)
|
||||
return s_execve;
|
||||
|
||||
if(strncmp(s_execve.filename, "sshd", 4) == 0)
|
||||
return s_execve;
|
||||
else
|
||||
return find_sshd_daemon(s_execve.ppid);
|
||||
}
|
||||
/*
|
||||
* This function get the localtime into the rsyslog format
|
||||
*/
|
||||
static int syslog_time(time_t ts, char t[32], size_t l){
|
||||
const char format[] = "%b %d %T";
|
||||
struct tm *lt = localtime(&ts);
|
||||
if(strftime(t, l, format, lt) == 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
int handle_event(void *ctx, void *data, size_t data_sz){
|
||||
struct event *s_event = (struct event *)data;
|
||||
struct execve s_execveParent = {0};
|
||||
struct utmp u = {0};
|
||||
time_t ts = time(NULL);
|
||||
char username[64];
|
||||
|
||||
// Fid the sshd process parent
|
||||
s_execveParent = find_sshd_daemon(s_event->ppid);
|
||||
if (s_execveParent.pid == 0){
|
||||
//printf("SSH daemon not exist\t%s\t%d\n", s_execve.filename, s_execve.pid);
|
||||
s_execveParent.ppid = walk_process_tree(s_event->pid); /* Walk through process tree to find the sshd daemon */
|
||||
if (s_execveParent.ppid <= 0) // We didn't find it
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the user related
|
||||
u = find_user_from_pid(s_execveParent.ppid);
|
||||
if (u.ut_type == 0)
|
||||
return 0;
|
||||
|
||||
// Get the username
|
||||
get_username(s_event->uid, username);
|
||||
|
||||
if (arguments.to_output == 1){
|
||||
char t[32];
|
||||
syslog_time(ts, t, sizeof(t));
|
||||
printf("%-20s%-10d%s@%-25s", t, s_execveParent.ppid, u.ut_user, u.ut_host);
|
||||
printf("%-10s%-10d", username, s_event->pid);
|
||||
printf("%-20s", s_event->filename);
|
||||
printf("%s ", s_event->comm);
|
||||
// Arguments
|
||||
if (s_event->argc > 0){
|
||||
for(int i = 0; i < s_event->tlen; i++){
|
||||
char c = s_event->args[i];
|
||||
if (c == '\0')
|
||||
printf(" ");
|
||||
else
|
||||
printf("%c", c);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (arguments.filename != NULL && f != NULL){
|
||||
/*
|
||||
* Rsyslog format
|
||||
* <time> <hostname> <procname>: <info> <data>
|
||||
*/
|
||||
char t[32];
|
||||
char host[294];
|
||||
char ppid[16];
|
||||
char command[ARGS_LEN * ARGS_CNT];
|
||||
char pid[16];
|
||||
char hname[128];
|
||||
if (syslog_time(ts, t, sizeof(t)) == 0)
|
||||
fwrite(t, strlen(t), 1, f);
|
||||
|
||||
snprintf(hname, 128, " %s", hostname);
|
||||
fwrite(hname, strlen(hname), 1, f);
|
||||
fwrite(" ssh-trace: ", 12, 1, f);
|
||||
fwrite("<info> ", 7, 1, f);
|
||||
|
||||
snprintf(host, 294, "host=%s@%s;", u.ut_user, u.ut_host);
|
||||
fwrite(host, strlen(host), 1, f);
|
||||
|
||||
snprintf(ppid, 16, "ppid=%d;", s_execveParent.ppid);
|
||||
fwrite(ppid, strlen(ppid), 1, f);
|
||||
|
||||
snprintf(command, ARGS_LEN * ARGS_CNT, "pathname=%s ", s_event->filename);
|
||||
if (s_event->argc > 0){
|
||||
char output[ARGS_TLEN];
|
||||
int i;
|
||||
for(i = 0; i < s_event->tlen; i++){
|
||||
char c = s_event->args[i];
|
||||
if (c == '\0')
|
||||
output[i] = ' ';
|
||||
else
|
||||
output[i] = c;
|
||||
}
|
||||
output[i] = '\0';
|
||||
strncat(command, output, strlen(output));
|
||||
}
|
||||
command[strlen(command) - 1] = ';';
|
||||
fwrite(command, strlen(command), 1, f);
|
||||
|
||||
snprintf(pid, 16, "pid=%d\n", s_event->pid);
|
||||
fwrite(pid, strlen(pid), 1, f);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
int main(int argc, char *argv[]){
|
||||
const char *fileObj = "src/ssh-trace.ebpf.o";
|
||||
struct bpf_object *obj;
|
||||
struct bpf_program *programProcessFork;
|
||||
struct bpf_program *programExecve;
|
||||
struct bpf_program *programExitExecve;
|
||||
struct ring_buffer *rb;
|
||||
// struct sshtrace *skel;
|
||||
int err;
|
||||
int fd_map_data;
|
||||
|
||||
arguments = parse_args(argc, argv); // Parsing arguments
|
||||
|
||||
signal(SIGINT, signalHandler);
|
||||
|
||||
/* Generate our skel for the ring buffer */
|
||||
/*skel = sshtrace__open();
|
||||
|
||||
if (!skel){
|
||||
printf("Failed to init skel\n");
|
||||
return -1;
|
||||
}*/
|
||||
|
||||
/* Open and load our eBPF object */
|
||||
obj = bpf_object__open_file(fileObj, NULL);
|
||||
if (!obj){
|
||||
printf("Failed to open the file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (err){
|
||||
printf("Failed to load object\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Retrieving fd of maps */
|
||||
fd_map_data = bpf_object__find_map_fd_by_name(obj, "data");
|
||||
if (!fd_map_data){
|
||||
printf("Failed to find the fd map data\n");
|
||||
bpf_object__close(obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd_map_execve = bpf_object__find_map_fd_by_name(obj, "execve_maps");
|
||||
if (!fd_map_execve){
|
||||
printf("Failed to find the fd map execve\n");
|
||||
bpf_object__close(obj);
|
||||
return -1;
|
||||
}
|
||||
fd_map_args = bpf_object__find_map_fd_by_name(obj, "execve_args");
|
||||
if (!fd_map_args){
|
||||
printf("Failed to find the fd map args\n");
|
||||
bpf_object__close(obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Retrieving our programs */
|
||||
programExecve = bpf_object__find_program_by_name(obj, "syscall_execve");
|
||||
programExitExecve = bpf_object__find_program_by_name(obj, "syscall_exit_execve");
|
||||
programProcessFork = bpf_object__find_program_by_name(obj, "syscall_process_fork");
|
||||
|
||||
if (!programExitExecve || !programExecve || !programProcessFork){
|
||||
printf("Failed to find program\n");
|
||||
bpf_object__close(obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bpf_program__attach(programProcessFork);
|
||||
bpf_program__attach(programExecve);
|
||||
bpf_program__attach(programExitExecve);
|
||||
|
||||
/* Start the ringbuffer */
|
||||
rb = ring_buffer__new(fd_map_data, handle_event, NULL, NULL);
|
||||
if (!rb){
|
||||
printf("Failed to create the ringbuf\n");
|
||||
bpf_object__close(obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (arguments.to_output == 1)
|
||||
printf("%-20s%-10s%-30s%-10s%-10s%-20s%s\n", "Datetime", "PID sshd", "Username@IP Address", "Username", "Pid", "Pathname", "Command");
|
||||
|
||||
if (arguments.filename != NULL){
|
||||
f = fopen(arguments.filename, "a");
|
||||
if (f == NULL){
|
||||
printf("Failed to create the file %s\n", arguments.filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the hostname */
|
||||
if (gethostname(hostname, 127) == -1){
|
||||
printf("Failed to get the hostname\n");
|
||||
strncpy(hostname, "ubuntu", 7);
|
||||
}
|
||||
|
||||
while(running){
|
||||
err = ring_buffer__poll(rb, 100 /* timeout, ms */);
|
||||
if (err == -EINTR){
|
||||
printf("Failed to get the ringbuf\n");
|
||||
running = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (f != NULL)
|
||||
fclose(f);
|
||||
ring_buffer__free(rb);
|
||||
bpf_object__close(obj);
|
||||
|
||||
return 0;
|
||||
}
|
204
src/ssh-trace.ebpf.c
Normal file
204
src/ssh-trace.ebpf.c
Normal file
@ -0,0 +1,204 @@
|
||||
#define BPF_NO_GLOBAL_DATA
|
||||
#define __TARGET_ARCH_x86
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include <string.h>
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
* Helper:
|
||||
* Issue: invalid indirect read from stack R2 off
|
||||
* Fix: check if all variables is initialised
|
||||
* Issue: R1 invalid mem access 'inv'
|
||||
* Fix: the value can be NULL
|
||||
*/
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint(max_entries, 256 * 1024 /* 256kb */);
|
||||
} data SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 32768); // pid_max -> https://linux.die.net/man/5/proc
|
||||
__type(key, pid_t);
|
||||
__type(value, struct execve);
|
||||
} execve_maps SEC(".maps");
|
||||
|
||||
struct ctx_execve {
|
||||
__u16 common_type; // Unsigned short
|
||||
__u8 common_flags; // Unsigned char
|
||||
__u8 common_preempt_count; // Unsigned char
|
||||
__s32 common_pid; // int
|
||||
__s32 syscall_nr; // int
|
||||
const __u8 *filename; // unsigned char
|
||||
const __u8 *const *argv;
|
||||
const __u8 *const *envp;
|
||||
};
|
||||
|
||||
struct ctx_fork {
|
||||
__u16 common_type; // Unsigned short
|
||||
__u8 common_flags; // Unsigned char
|
||||
__u8 common_preempt_count; // Unsigned char
|
||||
__s32 common_pid;
|
||||
char parent_comm[16];
|
||||
pid_t parent_pid;
|
||||
char child_comm[16];
|
||||
pid_t child_pid;
|
||||
};
|
||||
|
||||
/*
|
||||
* Made my own strncmp, because the function bpf_strncmp not available
|
||||
*/
|
||||
static int b_strncmp(const char *s1, __s32 len, const char *s2){
|
||||
__s32 c = 0;
|
||||
__s32 i;
|
||||
for (i = 0; i < len; i++){
|
||||
//while (c != '\0' || i != len){
|
||||
//c = s1[i];
|
||||
if (s1[i] == '\0')
|
||||
break;
|
||||
|
||||
if (s1[i] == s2[i])
|
||||
c++;
|
||||
}
|
||||
return (c == i) ? 0: 1;
|
||||
}
|
||||
/*
|
||||
* Need to detect the sshd forked, because, when a new user is connected
|
||||
* we cannot detect the execve sshd, but only the fork
|
||||
* Execve of the sshd is executed when the user is connected
|
||||
* But, if the user is authenticated, the process is forked
|
||||
*/
|
||||
SEC("tp/sched/sched_process_fork")
|
||||
int syscall_process_fork(struct ctx_fork *ctx){
|
||||
pid_t ppid;
|
||||
char child[16];
|
||||
pid_t pid = bpf_get_current_pid_tgid() >> 32;
|
||||
struct task_struct *t = (struct task_struct*)bpf_get_current_task();
|
||||
struct task_struct *real_parent;
|
||||
bpf_probe_read(&real_parent, sizeof(struct task_struct *), &t->real_parent);
|
||||
|
||||
bpf_probe_read(&ppid, sizeof(pid_t), &real_parent->pid);
|
||||
bpf_probe_read(&child, 16, &ctx->child_comm);
|
||||
|
||||
if (b_strncmp(child, 16, "sshd") == 0){
|
||||
struct execve s_execve = {0};
|
||||
s_execve.pid = pid;
|
||||
s_execve.ppid = ppid;
|
||||
bpf_probe_read(&s_execve.filename, sizeof(s_execve.filename), ctx->child_comm);
|
||||
// bpf_printk("Fork: %d %d", s_execve.pid, s_execve.ppid);
|
||||
|
||||
if (bpf_map_update_elem(&execve_maps, &pid, &s_execve, BPF_ANY) < 0){
|
||||
bpf_printk("Failed to update the map");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tp/syscalls/sys_exit_execve")
|
||||
int syscall_exit_execve(void) {
|
||||
pid_t ppid;
|
||||
struct task_struct *t = (struct task_struct*)bpf_get_current_task();
|
||||
struct task_struct *real_parent;
|
||||
bpf_probe_read(&real_parent, sizeof(struct task_struct *), &t->real_parent);
|
||||
bpf_probe_read(&ppid, sizeof(pid_t), &real_parent->tgid);
|
||||
// bpf_printk("Exit execve: %d %d", pid, ppid);
|
||||
return 0;
|
||||
}
|
||||
static pid_t walk_process_tree(struct task_struct *parent, pid_t pid){
|
||||
struct task_struct *r;
|
||||
pid_t p;
|
||||
|
||||
if(bpf_probe_read(&r, sizeof(struct task_struct *), &parent->real_parent) < 0)
|
||||
return 0;
|
||||
if(bpf_probe_read(&p, sizeof(pid_t), &r->tgid) < 0)
|
||||
return 0;
|
||||
return p;
|
||||
}
|
||||
static int execve(struct ctx_execve *ctx) {
|
||||
struct event *s_event;
|
||||
__u32 tlen = 0;
|
||||
const char *argv;
|
||||
// kuid_t kuid; /* https://elixir.bootlin.com/linux/v6.11.8/source/include/linux/uidgid_types.h#L9 */
|
||||
|
||||
s_event = bpf_ringbuf_reserve(&data, sizeof(*s_event), 0);
|
||||
if (!s_event)
|
||||
return 0;
|
||||
|
||||
s_event->pid = bpf_get_current_pid_tgid();
|
||||
s_event->ppid = 0;
|
||||
s_event->uid = bpf_get_current_uid_gid();
|
||||
|
||||
bpf_probe_read_user_str(&s_event->filename, sizeof(s_event->filename), ctx->filename);
|
||||
|
||||
// Arguments
|
||||
s_event->argc = 0;
|
||||
s_event->tlen = 0;
|
||||
|
||||
// Take the command executed
|
||||
long err = bpf_probe_read_user_str(&argv, sizeof(argv), &ctx->argv[0]);
|
||||
if (err < 0){
|
||||
bpf_printk("Failed to get the command");
|
||||
}
|
||||
else{
|
||||
__s32 len = bpf_probe_read_user_str(&s_event->comm, COMM_LEN, argv);
|
||||
if (len > COMM_LEN)
|
||||
bpf_printk("Cannot get the command");
|
||||
}
|
||||
// Start at one, because 0 is the command
|
||||
for (__s32 i = 1; i < ARGS_CNT; i++){
|
||||
err = bpf_probe_read_user_str(&argv, sizeof(argv), &ctx->argv[i]);
|
||||
|
||||
if (err < 0){
|
||||
//bpf_ringbuf_discard(s_event, 0);
|
||||
bpf_printk("Failed to read args");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* To avoid error:
|
||||
* Unbounded memory access, make sure to bounds check any such access
|
||||
*/
|
||||
if (tlen > (ARGS_TLEN - ARGS_LEN)){
|
||||
//bpf_ringbuf_discard(s_event, 0);
|
||||
bpf_printk("Max arguments exceeded: %d %d", i, tlen);
|
||||
break;
|
||||
}
|
||||
|
||||
__u32 len = bpf_probe_read_user_str(&s_event->args[tlen], ARGS_LEN, argv);
|
||||
if (len > ARGS_LEN){
|
||||
break;
|
||||
}
|
||||
tlen += len;
|
||||
s_event->argc++;
|
||||
}
|
||||
s_event->tlen = tlen;
|
||||
|
||||
// Get ppid
|
||||
struct task_struct *t = (struct task_struct*)bpf_get_current_task();
|
||||
s_event->ppid = walk_process_tree(t, s_event->pid);
|
||||
|
||||
/*if (bpf_ringbuf_output(&data, &s_event, sizeof(struct event), 0) != 0){
|
||||
bpf_printk("Failed to push into the ringbuf");
|
||||
return 0;
|
||||
}*/
|
||||
bpf_ringbuf_submit(s_event, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
SEC("tp/syscalls/sys_enter_execve")
|
||||
int syscall_execve(struct ctx_execve *ctx) {
|
||||
return execve(ctx);
|
||||
}
|
||||
/*SEC("tp/syscalls/sys_enter_mmap")
|
||||
int syscall_mmap(void){
|
||||
bpf_printk("mmap detected");
|
||||
return 0;
|
||||
}*/
|
||||
|
||||
char LICENSE[] SEC("license") = "GPL";
|
149942
src/vmlinux.h
Normal file
149942
src/vmlinux.h
Normal file
File diff suppressed because it is too large
Load Diff
BIN
ssh-trace
Executable file
BIN
ssh-trace
Executable file
Binary file not shown.
Loading…
Reference in New Issue
Block a user