Skip to content

execve · getuid · geteuid · access · open

A program running as root on behalf of somebody who is not

The setuid bit gives a program its owner's identity. Which of your two uids a given check uses is then the whole problem, and almost nobody can say which is which.

You have three uids and they are not the same

A process carries a real uid, an effective uid and a saved one. Ordinarily all three are you. When you run a program whose file is owned by root and has the setuid bit set, the effective uid becomes root and the real uid stays you.

Every filesystem permission check uses the effective uid. So the program can open anything root can open. The real uid is still there so the program can find out who actually ran it — and the whole job of a setuid program is to use the first responsibly on behalf of the second.

The saved uid is the third, and it exists so privilege can be put down and picked up again.

Try this
#include <unistd.h>

int main(void) {
  execve("/bin/tool", NULL, NULL);
  return 1;
}

Watch: The two numbers /bin/tool prints. One of them is not you.

setuid-and-the-deputy

`access` asks a different question than `open` answers

open uses the effective uid. access uses the real one. That is not an inconsistency — it is deliberate, and it exists precisely so a setuid program can ask "could the person who ran me have opened this?" before doing it on their behalf.

And it is a trap, because the answer is about a moment that has passed by the time you act on it. Between the access and the open, the path can become something else — a symlink to a file the real user could not read, which the program then opens as root and hands back.

The fix is never to ask. Drop the privilege, or open the file and handle the error: the kernel checks atomically and you cannot.

The trace gate catches this: faccessat followed by openat is the defect regardless of what either returned.

Try this
#include <unistd.h>

int main(void) {
  execve("/bin/tool", NULL, NULL);
  return 1;
}

Watch: That `access` says no and `open` says yes, on the same path, in the same program.

setuid-and-the-deputy

Before you run it

A setuid-root program wants to give up its privilege before doing something risky. It calls setuid(getuid()).

Predict

Can it get root back afterwards?

setuid-and-the-deputy

The order the two calls go in

Dropping both user and group is two calls, and the order matters more than anything else on this page. setgid needs privilege. setuid is what takes privilege away.

So the group goes first. A program that drops the user first cannot then drop the group, and keeps a group membership it meant to give up — often one that grants access to exactly the thing it was trying to protect.

Two lines, either order compiles, and one of them is a vulnerability.

setuid-and-the-deputy

Trace

Run one of the programs on the left.

Everything you do here stays in this browser.Part of liter8.sh