Cancel a system call with ptrace()

Viewed 4847

For some security purpose, I use ptrace to get the syscall number, and if it's a dangerous call (like 10 for unlink), I want to cancel this syscall.

Here's the source code for the test program del.c. Compile with gcc -o del del.c.

#include <stdio.h>
#include <stdlib.h>
int main()
{
    remove("/root/abc.out");
    return 0;
}

Here's the security manager source code test.c. Compile with gcc -o test test.c.

#include <signal.h>
#include <syscall.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <sys/user.h>
#include <sys/reg.h>
#include <sys/syscall.h>

int main()
{
    int i;
    pid_t child;
    int status;
    long orig_eax;
    child = fork();
    if(child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("/root/del", "del",  NULL);
    }
    else {
        i = 0;
        while(1){
            wait(&status);
            if (WIFEXITED(status) || WIFSIGNALED(status) )break;

            orig_eax = ptrace(PTRACE_PEEKUSER,
                          child, 4 * ORIG_EAX,
                          NULL);
            if (orig_eax == 10){
                fprintf(stderr, "Got it\n");
                kill(child, SIGKILL);
            }
            printf("%d time,"
               "system call %ld\n", i++, orig_eax);
            ptrace(PTRACE_SYSCALL, child, NULL, NULL);
        }
    }
    return 0;
}

Create the abc.out file, then run the test program:

cd /root
touch abc.out
./test

The file /root/abc.out should still exist.

How do I implement this requirement?

3 Answers

Paraphrasing https://nullprogram.com/blog/2018/06/23/

There is no way to cancel a system call once it's been initiated. However, you can modify the arguments to the system call, or its return value once it's returned. Hence, you can do something like this:

1) Wait for the traced process to invoke a system call.

2) Replace the system call number with something invalid. To do this, read the traced process's registers, modify them, and write them back to the process.

3) Continue the process; the system call will be executed, and the kernel will return an error since the process invoked an unimplemented system call.

4) When ptrace traces the return from the system call, you can optionally replace the kernel's error code with something else (e.g. replace the kernel's "unimplemented system call" with a "no permission error").

Related