aquasecurity / aquasecurity/tracee

ParseArgs unnecessary casting leads to wrong values

Open
#4,504 4 comments 1 reaction 0 assignees View on GitHub
kind/bug
Dominant language
Go
Stars
4.6k
Forks
507
Avg merge
2d 8h
Merged PRs (30d)
9

Description

## Description

I'll open an issue since I don't if the current approach is correct or not. Once we discuss it, I can submit a PR.

I was reviewing https://github.com/aquasecurity/tracee/pull/4442 when I realized that some casting might lead to incorrect values.

The syscall `execveat` has the field `flags` as type `int` in the definition:
https://github.com/aquasecurity/tracee/blob/8d22a0791f98d4a1df4e30c95bdb9d9eb3c6801f/pkg/events/core.go#L8073

But here happens a cast to `uint64` before passing `parseExecFlag`:
https://github.com/aquasecurity/tracee/blob/8d22a0791f98d4a1df4e30c95bdb9d9eb3c6801f/pkg/events/parse_args.go#L124-L129

The issue happens when we call `execveat` using `flag=-1`. The value will be converted to `0xFFFFFFFFFFFFFFFF`, setting all flags:
```
sudo ./dist/tracee -e execveat -s comm=python3.10
TIME UID COMM PID TID RET EVENT ARGS
11:29:25:554926 1000 python3.10 638196 638196 0 execveat dirfd: -100, pathname: /bin/ls, argv: [/bin/ls], envp: 0x0, flags: AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW|AT_EACCESS|AT_REMOVEDIR|AT_NO_AUTOMOUNT|AT_STATX_SYNC_TYPE|AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC|AT_RECURSIVE
11:29:25:554926 1000 python3.10 638196 638196 -22 execveat dirfd: -100, pathname: /bin/ls, argv: [/bin/ls], envp: 0x0, flags: AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW|AT_EACCESS|AT_REMOVEDIR|AT_NO_AUTOMOUNT|AT_STATX_SYNC_TYPE|AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC|AT_RECURSIVE
```

Usually, this behavior occurs when the type of the event in the definition is signed and we cast the negative value to unsigned. I think each function should have the signature aligned with the type of the field from the event defined in the definition.

## Output of `tracee version`:

```
(paste your output here)
```

## Output of `uname -a`:

```
(paste your output here)
```

## Additional details
Python code to call `execveat` with `flag=-1`.
```c
import ctypes
import errno
import os

SYS_EXECVEAT = 281 # ARM

AT_FDCWD = -100
exec_path = b"/bin/ls"
argv = (ctypes.c_char_p * 2)()
argv[0] = exec_path
argv[1] = None # Null-terminated argument list

# Environment variables (null-terminated array)
envp = (ctypes.c_char_p * 1)()
envp[0] = None

# trigger
flags = -1

libc = ctypes.CDLL("libc.so.6", use_errno=True)

ret = libc.syscall(SYS_EXECVEAT, AT_FDCWD, exec_path, ctypes.byref(argv), ctypes.byref(envp), flags)

if ret == -1:
err = ctypes.get_errno()
print(f"Error: {os.strerror(err)} (errno={err})")
if err == errno.ENOENT:
print("File not found.")
elif err == errno.EACCES:
print("Permission denied.")
else:
print("Unexpected error occurred.")
else:
print("Execveat syscall succeeded.")
```

Contributor guide

Open the contributing guide

Research direction

Start in pkg/events/parse_args.go at the parseExecFlag call and compare its casts with the execveat flags definition in pkg/events/core.go. Trace the signed-value path using the issue's flag=-1 reproduction; done means negative values are not converted into an all-bits-set unsigned value and the reported flags are correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, linux
Domain
operating-systems, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.