17

I found some surprising behavior on Ubuntu 14.04 when using strace on an executable, which I do not have read permission on. I wonder if this is a bug, or if some standard mandates this obscure behavior.

First let's see what happens when I start an ordinary executable in the background and attach to it. As expected this works:

$ /bin/sleep 100 &
[2] 8078
$ strace -p 8078
Process 8078 attached
restart_syscall(<... resuming interrupted call ...>

Next I try with an executable, which I have no read permissions on:

---x--x--x 1 root root 26280 Sep  3 09:37 sleep*

Attaching to this running process is not permitted:

$ ./sleep 100 &
[1] 8089
$ strace -p 8089
strace: attach: ptrace(PTRACE_ATTACH, ...): Operation not permitted

This is also what I would expect. Granting execute permission without read permission wouldn't do much good, if I could simply attach a debugger to the process and effectively have read permissions on the executable that way.

But if I start the executable under an already traced process, I am permitted to do so:

$ strace ./sleep 100
execve("./sleep", ["./sleep", "100"], [/* 69 vars */]) = 0
brk(0)                                  = 0x9b7a000

This is unexpected for me. Is this a security bug, or is it a feature mandated by a standard?

kasperd
  • 3,580
  • 3
    @StéphaneChazelas: The point is that he can ptrace it, by simply using it as argument to strace. The root cause seems to be that on execve calls, read permissions of the executed file are not checked again if the process is already traced. His question is whether that is a security bug or a mandated feature (if the latter, I'd still consider it a security bug, just a security bug of the specification). – celtschk Sep 03 '14 at 09:05
  • @celtschk, sorry, I read the question too quickly. – Stéphane Chazelas Sep 03 '14 at 09:15
  • 1
    The EPERM seems to come from get_dumpable() (used also to check whether core dumping is allowed, thus "dumpable") called from __ptrace_may_access() called from ptrace_attach() on kernel/ptrace.c. – ninjalj Sep 03 '14 at 10:06
  • When a program is running, will sufficient information be available to the debugger to generate a runnable executable containing its code, or will the program loader discard things like relocation fixups which would be needed to make a program actually work? – supercat Sep 03 '14 at 15:51
  • @supercat As far as I know, the debugger has access to single step through all the user mode code being executed, including the relocation code. With that level of access it shouldn't be too difficult to reproduce a working executable. – kasperd Sep 03 '14 at 16:11
  • @kasperd: Are you suggesting that the Unix execution model is more like a DOS "COM" file (read up to 64K into consecutive memory locations and then perform a CALL to to the first byte read) than a DOS "EXE" file (read in a list of code segments and fixups, load the segments, perform the fixups, and only start executing code after that's all done)? I would have expected Unix to do the latter. – supercat Sep 03 '14 at 16:49
  • @supercat No, I am not suggesting that the ELF format is that primitive. I am saying that I think the fixups are performed by library code rather than the kernel, and a debugger has full access to the state of the process before that library code is executed. – kasperd Sep 04 '14 at 20:16
  • @kasperd: Okay, that makes sense, though it would make less meaningful the possibility of a file allowing execute access but not read access. If executable files had to be loaded by a routine which couldn't be stepped into by the debugger, that could provide better security for executable files. – supercat Sep 04 '14 at 20:33
  • it is a security feature – Skaperen Apr 25 '15 at 00:20
  • @Skaperen Who exactly is that comment addressed to? – kasperd Apr 25 '15 at 07:59

1 Answers1

7

This is not an answer, rather a collection of links and thoughts in case someone else would like to study as well. Because this is quite an interesting thing.

Related answer on Unix&Linux mentioning it is (or was, can't test with vanilla kernel right now) possible to dump read only binaries this way.

Grsecurity was trying to fix this config option and the patch itself (altough it may have changed since)

This commit really makes it seem that, kernel developers really care only about dumping suid binaries.

But actually from this line I'd guess kernel wants to prevent dumping unreadable binaries regardles of SUID status. And this line suggests that binaries which are not dumpable should not be traceable.

So at first sight it seems you have found a bug in kernel with security implications. But I am no kernel developer, so I can not say for sure. I would ask on LKML.

Edit: one more finding, with regards to debugger, mentioned in comments to original post - from quick stracing (again) it seems to me, that gdb uses the traced binaries and /proc/<pid>/mem. Once the running binary is not readable, cat /proc/<pid>/mem returns EPERM. If the binary is readable, it returns EIO. (Tested this on Ubuntu 14.10, which runs several security patches, so this might be different from vanilla kernel. Again I do not have vanilla kernel running anywhere handy :()

Fox
  • 466