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.

args[0]char* filenameargs[1]char* argv[]args[2]char* envp[]secret_app…argv[0]./secret_ap…argv[1]--password\0argv[2]hunter2\0NULLenvp[0]PATH=/usr/b…envp[1]HOME=/homeNULL

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:

  1. Get the argv pointer from ctx->args[1]
  2. Cast it from unsigned long to char **
  3. Read each pointer in the array (from user space)
  4. 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:

  1. Iterate through the argv array (cap iteration to 10 arguments)
  2. Find the argument that contains "--password"
  3. Read the next argument, which contains the actual password
  4. Submit it using SUBMIT_STR_LEN
Quick reference
Compare two strings for equality
Function bpf_strncmp Full documentation
Args:
char* bufdynamic buffer to compare
u32 buf_szlength of dynamic buffer
const char* buf2literal string to compare against
Returns 0 if strings match, non-zero if they differ
Read string from user space into kernel buffer
Function bpf_probe_read_user_str Full documentation
Args:
void* dstkernel buffer to read into
u32 sizemaximum bytes to read
const void* srcuser space pointer to string
On success, returns number of bytes read (including null terminator)
On error, returns negative error code
Read bytes from user space into kernel buffer
Function bpf_probe_read_user Full documentation
Args:
void* dstkernel buffer to read into
u32 sizebytes to read
const void* srcuser space pointer
On success, returns 0
On error, returns negative error code
Run your code to see execution events here