2

When doing privilege escalation, assuming an application with the SUID set and a debugger, what stops us from starting a shell from within the debugger? I mean just write the shell code in an environment variable or somewhere in memory and just call the pointer from within the debugger. Or in any case, if that would not work we could just manually change a return address on the stack and possibly even detach afterwards.

I think I am missing something but I can't quite see what it is. My best guess would be that when a debugger is attached the SUID bit is cleared otherwise this is a very generic way of escalating privileges.

alex10791
  • 151
  • 1
  • 4
  • Note that this has also been answered in https://stackoverflow.com/questions/21337923/why-ptrace-doesnt-attach-to-process-after-setuid. – forest Dec 31 '17 at 04:20

2 Answers2

3

what stops us from starting a shell from within the debugger?

If you run a setuid program under debugger, and you're not root, it will run as if was not setuid. Meaning it will execute under your id, not root. So while you can spawn a shell, it will use your credentials.

You can easily test it yourself by launching su under gdb and verify in another console that it is executed under your user account.

And if you launch the setuid program and then try to gdb attach to it, this will return 'permission denied' error.

George Y.
  • 3,504
  • 2
  • 10
  • 15
  • 1
    According to `ptrace(2)`, a process which does not have the "dumpable" attribute set to something other than 1, and the caller does not have `CAP_SYS_PTRACE`, the syscall will return `-EPERM`. – forest Dec 31 '17 at 11:42
  • 1
    I'm not really sure how your comment applies to my answer, honestly. OP didn't ask about ptrace at all. And ptrace is Linux-specific while the logic is not (nor OP seem to ask about Linux as its only mentioned in tags) – George Y. Dec 31 '17 at 16:16
  • 1
    Linux was in the tags so it's only proper to give a Linux-specific answer (even if touching on generalities is fine as well). And debuggers on Linux use `ptrace()` (e.g. gdb, rr, etc). It's the only software way to set breakpoints, step through instructions, view register states, etc. – forest Dec 31 '17 at 23:24
  • I'm not sure ptrace is relevant here at all. If what you quoted about ptrace above was correct, one would be unable to run SUID programs under gdb at all. This is not the case however - they start fine, just under your userid. – George Y. Dec 31 '17 at 23:41
  • 1
    Read the gdb source code. Or trace it. Or [look it up](https://en.wikipedia.org/wiki/Ptrace). It uses `ptrace()` internally. What other syscall could it _possibly_ use? It has `PEEKUSER`, `POKEUSER`, `GETREGS`, `SETREGS`, `SYSEMU_SINGLESTEP`, etc. Note also that gdb works by forking and then doing an `execve()` of the program, giving it complete control. As such, it can make the target program do a `ptrace(PTRACE_TRACEME)` in the tracee to get around that issue while still not risking security. This is also how strace does it. – forest Dec 31 '17 at 23:50
  • Sorry but you missed it completely. Both this question and my answer were not about gdb internals at all. Its you keep bringing up ptrace over and over again. – George Y. Dec 31 '17 at 23:54
  • Then tell me what Linux debugger can be used which does not use `ptrace()`? Tell me how anyone would possibly do instruction stepping on Linux without it? Virtually _all_ debugging utilities use it. I'm not aware of a single one which does not. I brought up `ptrace()` because it is _the_ syscall used for debugging under Linux. If the tag was Solaris, then I'd have answered differently. – forest Dec 31 '17 at 23:56
  • Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/71027/discussion-between-george-y-and-forest). – George Y. Dec 31 '17 at 23:58
3

Only root can attach to a process with elevated privileges. In a bit more detail, attaching to a running process with the ptrace system call (which is what debuggers use under the hood) requires several conditions. A necessary condition, the classical Unix condition, is that if the target process is running without elevated privileges (no setuid, no setgid, and no other privilege elevation mechanism such as setcap), then the process calling ptrace must be running as the same user ID. A process running as root (effective user ID equal to 0) can always attach, so root can debug any process.

(Linux has additional restrictions depending on its hardening state; most distributions set the kernel.yama.ptrace_scope to 1, which prevents any non-root process from running ptrace except to trace its child processes. This means you can run a program in a debugger but not attach a debugger to an already-running process.)

Furthermore, if a process is being traced (i.e. being debugged), then it cannot elevate its privileges. Privilege elevation is performed by the execve system call, based on the permission flags in the executed file. If a process calls execve while it is being traced, the privilege elevation flags are ignored, and the new process image has the same privileges as it had before the execve call.

This way, privilege elevation is incompatible with anything that could change the security implications. There's no way to arrange to run a debugger on a process with extra privileges, unless you run the debugger as root (if you can do that then you won't be getting any privileges that you didn't already have).

If you want to debug a program that runs with elevated privileges, there are two ways, both requiring that the debugger runs as root. You can start the debugger and make it run the whole preparation code that sets up the privileges with which the process will run. Or else you can let the program start under its normal conditions, and attach the debugger to it at any point.

Of course, if a program starts as setuid and then drops privileges (i.e. ends up with equal effective, real and saved user IDs, and likewise for group IDs and any other privileges), then you can attach a debugger running under the same user after the process has dropped its privileges.

Gilles 'SO- stop being evil'
  • 50,912
  • 13
  • 120
  • 179