Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: filldir64 event #4588

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

rscampos
Copy link
Collaborator

@rscampos rscampos commented Feb 13, 2025

1. Explain what the PR does

6482680 fix: filldir64 event

The second parameter (`name`) of filldir64 is currently being
interpreted as the `process name`, whereas it should be
interpreted as the directory entry name (`dirent name`).

2. Explain how to test it

In order to generate the event hidden_inodes its necessary to trigger filldir64. filldir64 is triggered by getdents64 (library function readdir). Flow: readdir -> getdents64 -> filldir64

% gcc readdir -o readdir.c
% ./readdir /

readdir source code:

Source code
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>

int main(int argc, char *argv[]) {
    // Check if the directory path is provided
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <directory_path>\n", argv[0]);
        return EXIT_FAILURE;
    }
    // Open the directory
    DIR *dir = opendir(argv[1]);
    if (dir == NULL) {
        perror("opendir");
        return EXIT_FAILURE;
    }
    printf("Contents of directory %s:\n", argv[1]);
    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        printf("Name: %s", entry->d_name);
        // Additional information
        switch (entry->d_type) {
            case DT_REG:
                printf(" (Regular file)\n");
                break;
            case DT_DIR:
                printf(" (Directory)\n");
                break;
            case DT_LNK:
                printf(" (Symbolic link)\n");
                break;
            default:
                printf(" (Other)\n");
                break;
        }
    }
    // Close the directory
    closedir(dir);
    return EXIT_SUCCESS;
}
sudo ./dist/tracee -s comm=readdir -e hidden_inodes.data.hidden_dirent=tmp
TIME             UID    COMM             PID     TID     RET              EVENT                     ARGS
08:12:55:823956  1000   readdir          2470122 2470122 0                hidden_inodes             hidden_dirent: tmp

3. Other comments

Notes: this C example is a partial PoC because don't trigger the event unless we comment out the lines:

    // only inode=0 is relevant, simple filter prior to program run
    unsigned long dirent_inode_number = (unsigned long) PT_REGS_PARM5(ctx);
    if (dirent_inode_number != 0)
        return 0;

For a fully PoC would be necessary to trigger the event and some how set the inode=0 before calling filldir64. Maybe using a LKM to hook filldir64 and set to 0 before passing to the original function.

@rscampos
Copy link
Collaborator Author

@OriGlassman FYI

@rscampos rscampos requested a review from geyslan February 13, 2025 14:30
@rscampos rscampos added this to the v0.24.0 milestone Feb 13, 2025
@geyslan geyslan removed this from the v0.24.0 milestone Feb 13, 2025
The second parameter (`name`) of filldir64 is currently being
interpreted as the `process name`, whereas it should be
interpreted as the directory entry name (`dirent`).
@rscampos rscampos force-pushed the fix_filldir64_event branch from 6482680 to b830f86 Compare February 14, 2025 12:12
@@ -1674,8 +1674,8 @@ SEC("kprobe/filldir64")
int BPF_KPROBE(trace_filldir64)
{
// only inode=0 is relevant, simple filter prior to program run
unsigned long process_inode_number = (unsigned long) PT_REGS_PARM5(ctx);
if (process_inode_number != 0)
unsigned long dirent_inode_number = (unsigned long) PT_REGS_PARM5(ctx);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes align with the probed function, but I suspect the probe name is misleading. It only submits events related to zeroed inodes (a corner case), which, in the case of procfs, would suppress output for the associated processes. To avoid confusion for those encountering this probe, renaming the probe appropriately would be great too.

@@ -11900,7 +11900,7 @@ var CoreEvents = map[ID]Definition{
},
sets: []string{},
fields: []trace.ArgMeta{
{Type: "char*", Name: "hidden_process"},
{Type: "char*", Name: "hidden_dirent"},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{Type: "char*", Name: "hidden_dirent"},
{Type: "char*", Name: "dirent_name"},

Although the event is used to catch hidden processes, the arg field is the name of the dirent with zeroed inode.

The event, in my opinion, should be called ZeroedInodes, since whoever indirectly calls filldir64 may pass a zeroed inode. @NDStrahilevitz WDYT?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if research uses this event "raw" or passes it to a signature for further inspection. So their input is the relevant one. @OriGlassman @oshaked1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used as data filter.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As pointed out by @geyslan in this reference, always assuming that inode=0 is explicitly used by an attacker to hide an inode may lead to a false positive... if unused dir uses inode=0 as described:

The end of the entire directory is of course signified by reaching the end of the file. Unused directory entries are signified by inode = 0.

Maybe one possibility is changing the name as Geyslan suggested.

There is one helper from research which is using the field interpreting as hidden process whereas should be hidden dirent or unused dirent or something similar.

@geyslan
Copy link
Member

geyslan commented Feb 19, 2025

Postponing this, since we need to release v0.23.0 asap. @rscampos

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants