Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/kern Harden the NetBSD PT_TRACE_ME operation



details:   https://anonhg.NetBSD.org/src/rev/556440f10907
branches:  trunk
changeset: 318553:556440f10907
user:      kamil <kamil%NetBSD.org@localhost>
date:      Sun Apr 29 04:28:09 2018 +0000
description:
Harden the NetBSD PT_TRACE_ME operation

You can't say to the parent of a process to start tracing if:
        (1) the parent is initproc,
        (2) the child is already traced.

Rationale:
 (1) - It has a side effect of being an anti-debugger functionality,
       as we cannot kill initproc (PID1) and reset the traced flag.
     - initproc is not a debugger, raising debugging events from a child
       to initproc can result in at least a stopped/hanging process
       in the system.
 (2) - It does not make sense to be simultanously traced by two debuggers.
     - It does not make sense to be traced twice by the same debugger.

Permit enable tracing for a parent that has been chroot(8)ed, as this is
harmless and the parent is already monitoring for child signals.

The same semantics exist in FreeBSD.

If you are looking for an antidebugging trick for old NetBSD (pre 8.0)
or other popular kernels, here is an example:

$ cat antidebug.c
#include <sys/types.h>
#include <sys/ptrace.h>

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int
main(int argc, char **argv)
{
        pid_t child;
        int rv;
        int n = 0;

        child = fork();
        if (child == 0) {
                while (getppid() != 1)
                        continue;
                rv = ptrace(PT_TRACE_ME, 0, 0, 0);
                if (rv != 0)
                        abort();
                printf("Try to detach to me with a debugger!! ");
                printf("haha My PID is %d\n", getpid());
                while (1) {
                        printf("%d\n", n++);
                        sleep(1);
                }
        }
        exit(0);
}

A developer is no longer able to attach GDB, strace or LLDB to this program
without killing the initproc (your favourite system daemon).. this action
would be fatal for the operation of the whole Operating System stability.

Examples from a current non-NetBSD popular kernel:

$ ps -o ppid= -p 17904
    1

$ strace -p 17904
strace: attach: ptrace(PTRACE_SEIZE, 17904): Operation not permitted

$ gdb -p 17904
[...]
Attaching to process 17904
warning: process 17904 is already traced by process 1
ptrace: Operation not permitted.
(gdb)

$ lldb-3.9 -p 17904
(lldb) process attach --pid 17904
error: attach failed: unable to attach


On NetBSD 8.0 and newer it is now guaranteed to have an option to kill
a malevolent (fake?) debugger and attach with a new tracer to the process.


Sponsored by <The NetBSD Foundation>

diffstat:

 sys/kern/sys_ptrace_common.c |  20 ++++++++++++++++----
 1 files changed, 16 insertions(+), 4 deletions(-)

diffs (48 lines):

diff -r 95842f1cdfa7 -r 556440f10907 sys/kern/sys_ptrace_common.c
--- a/sys/kern/sys_ptrace_common.c      Sat Apr 28 20:35:05 2018 +0000
+++ b/sys/kern/sys_ptrace_common.c      Sun Apr 29 04:28:09 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: sys_ptrace_common.c,v 1.37 2018/04/27 16:50:56 kamil Exp $     */
+/*     $NetBSD: sys_ptrace_common.c,v 1.38 2018/04/29 04:28:09 kamil Exp $     */
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -118,7 +118,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.37 2018/04/27 16:50:56 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.38 2018/04/29 04:28:09 kamil Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ptrace.h"
@@ -377,7 +377,19 @@
        /* Make sure we can operate on it. */
        switch (req) {
        case PT_TRACE_ME:
-               /* Saying that you're being traced is always legal. */
+               /*
+                * You can't say to the parent of a process to start tracing if:
+                *      (1) the parent is initproc,
+                */
+               if (p->p_pptr == initproc)
+                       return EPERM;
+
+               /*
+                *      (2) the child is already traced.
+                */
+               if (ISSET(p->p_slflag, PSL_TRACED))
+                       return EBUSY;
+
                return 0;
 
        case PT_ATTACH:
@@ -389,7 +401,7 @@
                        return EINVAL;
 
                /*
-                *  (2) it's a system process
+                *      (2) it's a system process
                 */
                if (t->p_flag & PK_SYSTEM)
                        return EPERM;



Home | Main Index | Thread Index | Old Index