2

Consider running the following Python code as root:

import os
f=os.open("/etc/shadow", os.O_RDONLY)
os.setuid(65535)
os.open(f"/proc/self/fd/{f}", os.O_RDONLY)

Here is a one-liner convenient for pasting:

python3 -c 'import os; f=os.open("/etc/shadow", os.O_RDONLY); os.setuid(65535); os.open(f"/proc/self/fd/{f}", os.O_RDONLY)'

Given the comment of proc_fd_permission, I would expect this code to succeed. However, I actually observe -EACCES. Why is this use of /proc/self/fd/N not permitted and what is the source code comment actually trying to convey?

Update: If the permission only applies to the symlink itself and not the target file, why can I open sockets and deleted files via /proc/self/fd/N? (e.g. exec 3>foo; echo hello >&3; rm foo; cat /proc/self/fd/3 prints hello)

asked Feb 24, 2023 at 15:45
7
  • 1
    f"/proc/self/fd/{f}" will be a symbolic link to /etc/shadow which the process no longer has permission to open once it has dropped its privileges. Commented Feb 24, 2023 at 17:06
  • You still have permission to read the symbolic link: link=os.readlink(f"/proc/self/fd/{f}"); print(link) but that does not grant you permission to open the file that the link points to. Commented Feb 24, 2023 at 17:16
  • Of course, if you first resolve the link and then apply the permission of the target file, -EACCES is the natural outcome. But what is the benefit of proc_fd_permission and its special handling? If the link is just followed, then how does opening a deleted file work? Consider exec 3>foo; echo hello >&3; rm foo; cat /proc/self/fd/3 which will print hello. Commented Feb 25, 2023 at 18:40
  • 1
    proc_fd_permission is applied to inodes of directories /proc/PID/fd, not inodes of files/symlinks /proc/PID/fd/*, you got -EACCES most likely due to proc_fd_access_allowed (elixir.bootlin.com/linux/latest/source/fs/proc/base.c#L1752). You still can list files in directory /proc/self/fd after os.setuid(65535), that's the purpose of proc_fd_permission. it is setup here (elixir.bootlin.com/linux/latest/source/fs/proc/base.c#L3591) Commented Feb 26, 2023 at 10:21
  • This latter comment of @k1r1t0 actually sheds some light on the issue. It highlights that the actual check is in https://elixir.bootlin.com/linux/latest/source/fs/proc/base.c#L672 and that we require ptrace capability. That raises the next question: Why is my process unable to ptrace itself? Commented Feb 28, 2023 at 5:15

1 Answer 1

-1

Inside your program, open files are represented as integer file descriptors. These are the filenames of the entries in the directory: /proc/self/fd. The kernel's /proc/ ABI helps you debug your system by displaying what those file descriptors refer to using the conventions of symlinks. But those symlinks are not equivalent to the open file descriptor.

$ ls -l /proc/self/fd
total 0
lrwx------. 1 tinkerer tinkerer 64 Mar 10 08:19 0 -> /dev/pts/0
lrwx------. 1 tinkerer tinkerer 64 Mar 10 08:19 1 -> /dev/pts/0
lrwx------. 1 tinkerer tinkerer 64 Mar 10 08:19 2 -> /dev/pts/0
lr-x------. 1 tinkerer tinkerer 64 Mar 10 08:19 3 -> /proc/230669/fd

If you close a file descriptor, there is no guarantee you can open it again. You have to satisfy the kernel's permission model to do that at the time you attempt to open the file. When you change UID, you should expect the permission model to view the open request differently.

If you want a second file descriptor to point to an already open file (descriptor), you should use the os.dup() method.

answered Mar 10, 2023 at 16:26
Sign up to request clarification or add additional context in comments.

1 Comment

If I had to satisfy the kernel's permission model in all cases, it would be impossible to open deleted files or network sockets via /proc/self/fd/... both of which are in fact possible. As such that statement is relatively obviously wrong and with it, your answer becomes relatively useless. It also is obvious that dup2 is the way to go if possible. That's not the question about that. It just is quite hard to shoehorn dup2 into existing ELF binaries. Hence the question of "fooling" open.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.