setuid funktioniert nicht mit dem Standardbenutzerkonto

setuid funktioniert nicht mit dem Standardbenutzerkonto

Schauen Sie sich dieses C-Programm an:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    printf("UID:  %d\n", getuid());
    printf("EUID: %d\n", geteuid());
    system("id");

    printf("res=%d\n", setuid(1001));

    printf("UID:  %d\n", getuid());
    printf("EUID: %d\n", geteuid());
    system("id");
    return 0;
}

Mein Benutzerkonto ist „test“ (ID 1000). Ich habe ein zweites Benutzerkonto: „test2“ (ID 1001).

Folgendes habe ich getan:

gcc test.c -o ./a.out
sudo chown test2 ./a.out
sudo chmod u+s ./a.out

Folgendes passiert, wenn ich ./a.out starte:

UID:  1000
EUID: 1001
uid=1000(test) gid=1000(test) groups=1000(test),...
res=0
UID:  1000
EUID: 1001
uid=1000(test) gid=1000(test) groups=1000(test),...
          

Ich verstehe nicht, warum ich im zweiten Teil nicht uid=1001 sehe …

Ich habe Folgendes versucht:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    printf("UID:  %d\n", getuid());
    printf("EUID: %d\n", geteuid());
    system("id");

    printf("res=%d\n", setuid(0));  // <- Here is the change

    printf("UID:  %d\n", getuid());
    printf("EUID: %d\n", geteuid());
    system("id");
    return 0;
}
          
gcc test.c -o ./a.out
sudo chown root ./a.out
sudo chmod u+s ./a.out

Folgendes erhalte ich, wenn ich ./a.out ausführe:

UID:  1000
EUID: 0
uid=1000(test) gid=1000(test) groups=1000(test),...
res=0
UID:  0
EUID: 0
uid=0(root) gid=1000(test) groups=1000(test),...
           

Jetzt funktioniert es.

Daher verstehe ich nicht, warum Setuid mit dem Benutzer Test2 nicht funktioniert, mit Root-Benutzern jedoch funktioniert ...

Irgendeine Idee ?

Danke

Antwort1

Hier passieren ein paar Dinge, die diese Verwirrung verursachen. Setuid funktioniert in beiden Fällen, es funktioniert nur nicht so, wie Sie es erwarten. Erstens gibt es das Problem der echten und effektiven Benutzer-IDs. Laut derman-Seite für setuid(2)

Wenn der aufrufende Prozess privilegiert ist (genauer: wenn der Prozess die CAP_SETUIDBerechtigung in seinem Benutzernamensraum hat), werden auch die echte UID und die gespeicherte Set-User-ID gesetzt.

Dies bedeutet auch, dass der aufrufende Prozessnichtdie Fähigkeit haben CAP_SETUID(was bei Ihren normalen Benutzern der Fall ist), dann wird die UIDnichtändern, nur die EUID.

Kommen wir also zum system()Anruf. Anrufen system("anything")ist das Äquivalent zu Folgendem:

execl("/bin/sh", "sh", "-c", "anything", (char *) NULL);

Es erzeugt also /bin/sh, liefert Argumente, damit eine neue Shell Ihren Befehl ausführt. Ich nehme an, Sie verwenden Bash als Shell, weil Bash so funktioniert

Wenn die Shell mit einer effektiven Benutzer- (Gruppen-)ID gestartet wird, die ungleich der tatsächlichen Benutzer- (Gruppen-)ID ist, und die Option -p nicht angegeben ist, (...) wird die effektive Benutzer-ID auf die tatsächliche Benutzer-ID gesetzt.

Wenn Ihr Prozess also nicht über diese CAP_SETUIDFähigkeit verfügt, wird er nicht die echte Benutzer-ID ändern, sondern nur die effektive UID. Wenn dann Bash gestartet wird, wird die effektive UID gelöscht, da UID nicht gleich EUID ist. Sie können dies bestätigen, indem Sie aufrufen

system("touch /tmp/blah")

und Hinzufügen

FILE *file = fopen("/tmp/blah2", "w+");
fclose(file);

zu Ihrem Programm. Sie werden sehen, dass der Besitzer /tmp/blah„test“ sein wird (da die EUID bei der Shell-Ausführung gelöscht wird), aber /tmp/blah2„test2“ gehören wird.

verwandte Informationen