what is ptrace

ptrace() system call stands for process trace, which provides a way for debuggers such as gdb/strace to control a process (tracee). "debuggers" can be any process that sends a PTRACE_ATTACH/PTRACE_SEIZE, or receives a PTRACE_TRACEME from its child.

several things to notice:

  1. a tracee's ptrace relationship can be found in tracee->ptracer_cred, which, holds tracer's credentials. in the credentials we care about uid/gid, that decides what this process can do
  2. under ptrace, execve() cant set-UID when executing SUID programs in current ptraced process. and yes, it allows set-UID without ptrace
  3. but if the tracee had a privileged tracer, whose uid is stored in tracee->ptracer_cred, indicating that the tracee is being traced by a privileged process, the tracee can set-UID to the whatever UID the SUID program holds. its like, when you launch gdb with root, tracing an unprivileged process that executes an SUID program, it has to be allowed, right?
  4. as for 2, the reason is when using ptrace, we can replace the tracee's SUID program with something we control, and keeps the privileged process, meaning we get root

other things you might need to read about:

the bug

now we know from note 3, the tracee->ptracer_cred decides if the tracee's parent is privileged, if yes, we can set-UID.

actually in kernel/ptrace.c (you need to checkout the old unpatched version of course), a child is able to grab a privileged parent, and force it to become a tracer, thus get a privileged ptrace relationship


we can take advantage of this bug, but it can be challenging

the exploit

the author ( wrote a beautiful PoC to exploit this bug, can be hard to understand at once. but basically its like:

          forks            forks
--proc_A ------> proc_B ------------> proc_C
|   |              |                    |
|  wait B          |                    |
|  and attach      |                    |
|  execve stage 2  |                    |
|  in B            |                    |
|                  |                    |
stage 1          pkexec               get privileged tracer
|                  |                    |
|                  |                    |
|                unprivileged         exec SUID binary,
|                traced by A          become privileged, SIGTRAPed
|                  |                    |
stage 2          wait C,                |
|                execve stage3 in C     |
|                                       |
|                                       |
|                                       |
stage 3                               setresuid to 0, 0, 0
|                                       |
|                                     exeve bash as root :)


comments powered by Disqus