Concept familiarization
Reading syscall arrays
In the previous exercise, you learned how to read the filename argument from the execve system call using ctx->args[0].
Now let’s explore the second argument: argv. When you run a command like:
./secret_app --password hunter2
The kernel receives an array of arguments:
argv[0]="./secret_app"argv[1]="--password"argv[2]="hunter2"argv[3]=NULL(terminator)
Looking at the execve manual page:
int execve(const char *filename, char *const argv[], char *const envp[]);
The second parameter, argv, is located at ctx->args[1].
Unlike filename, which was a simple pointer to a string, argv and envp are pointers to an array of pointers.
As you can see in the diagram, argv and envp are terminated by a NULL pointer.
When you read an entry from the argv array (e.g., argv[i]), check if it’s NULL before using it, as this marks the end of the array.
Reading the array of pointers
To read argv, we need to:
- Get the argv pointer from
ctx->args[1] - Cast it from
unsigned longtochar ** - Read each pointer in the array (from user space)
- For each pointer, read the string it points to (also from user space)
Here’s the basic structure:
// Get the argv pointer
char **argv = (char **)ctx->args[1];
// In a loop, read the next pointer from the array
char *arg_ptr;
bpf_probe_read_user(&arg_ptr, sizeof(arg_ptr), &argv[i]);
// Remember to check for the NULL terminator!
// Read the string that pointer points to
char arg_buf[64];
bpf_probe_read_user_str(arg_buf, sizeof(arg_buf), arg_ptr);
Remember that the loop must have a fixed upper bound (like i < 10) because the verifier needs to prove your program will terminate.
The challenge
A suspicious program is being executed with a --password flag followed by the actual password.
You will need to:
- Iterate through the
argvarray (cap iteration to 10 arguments) - Find the argument that contains
"--password" - Read the next argument, which contains the actual password
- Submit it using
SUBMIT_STR_LEN
Compare two strings for equality
- Args:
char* bufdynamic buffer to compareu32 buf_szlength of dynamic bufferconst char* buf2literal string to compare against
Read string from user space into kernel buffer
- Args:
void* dstkernel buffer to read intou32 sizemaximum bytes to readconst void* srcuser space pointer to string
Read bytes from user space into kernel buffer
- Args:
void* dstkernel buffer to read intou32 sizebytes to readconst void* srcuser space pointer