Permission denied when opening a file in gdb

I’m implementing some CTF challenges. The flags are in some text files, that get read from the programs. To protect the flags I have changed the owner of the files, but have set the setuid to the executables to be able to read the files. It works when I run my programs outside gdb, and the flags are read, but inside gdb I get Permission denied.
I’m running the exercises inside a Linux virutal machine in VirtualBox. I have created a normal user that is not in the sudoers file, and the flags files belong to root.

-rwsr-xr-x 1 root user 15260 Mar 13 13:22  exercise6
-rw-r--r-- 1 user user  3270 Mar 13 06:10 'Exercise 6.c'
-rwsr-xr-x 1 root user 15700 Mar 14 03:28  exercise7
-rw-r--r-- 1 user user  4372 Mar 13 06:10 'Exercise 7.c'
-rwS------ 1 root root 28 Mar 13 06:10 admin_flag.txt
-rwS------ 1 root root 20 Mar 13 06:24 exercise1.txt
-rwS------ 1 root root 27 Mar 13 06:24 exercise2.txt
-rws------ 1 root user 18 Mar 13 10:34 exercise3.txt
-rwS------ 1 root root 22 Mar 13 06:24 exercise4.txt
-rwS------ 1 root root 19 Mar 13 06:10 user_flag.txt
Asked By: r3k0j

||

The security contract of setuid¹ is that it grants the executable program extra privileges. Those privileges are only granted to the program. They must not allow the invoking user to do anything that the program won’t do.

This makes setuid incompatible with tracing (the ptrace system call on most Unix variants). If the invoking user can observe the internal operation of the program, that gives them access to any confidential data that the program has access to. But this may be confidential data that the user shouldn’t be allowed to see, which the program doesn’t normally reveal. Perhaps more obviously, if the invoking user can change what the program is doing (also ptrace), this could allow them to completely have all the privileges of the setuid user, which completely defeats the objective of only granting permission to run one specific program. Tracing is the functionality that a debugger such as GDB uses to inspect and control the execution of the program.

As an example, consider the program unix_chkpwd, whose job is to check the password of the invoking user. This program must be able to read confidential data (the password hash database). But the invoking user must not be allowed to read the whole database. The invoking user must only be able to query the entry for that user, and only with a yes/no answer (no extraction of the password hash itself), and with a rate limitation to prevent brute-force cracking. If you could run gdb unix_chkpwd and print out the content of the password hash database, it would completely break the security of that database.

In order to maintain security:

  • When a program is already being traced when it starts, the setuid bit is ignored. The executable starts with no extra privileges.
  • Only root is allowed to trace a program that was started with extra privileges.

¹ This also applies to setgid, setcap or any other similar mechanism to run a program with elevated privileges.