0
➜ go version      
go version go1.19.6 linux/amd64

➜ uname -a
Linux dmitry-desktop 6.1.18-200.fc37.x86_64 #1 SMP PREEMPT_DYNAMIC Sat Mar 11 16:09:14 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

I am unable to run compiled golang app using setuid/setguid bits of the executable file. I have set +s permission on the executable main file, owned by test user. I'm trying to invoke that file from dmitry user expecting the file would use test uid, but it's not happening.

[test@dmitry-desktop setguid]$ whoami
test
[test@dmitry-desktop setguid]$ id
uid=1001(test) gid=1002(test) groups=1002(test) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[test@dmitry-desktop setguid]$ ./main
[test@dmitry-desktop setguid]$ ls -la
total 1356
drwxrwxr-x.  2 test test     100 Mar 26 14:07 .
drwxrwxrwt. 48 root root    1780 Mar 26 14:07 ..
-rwsr-sr-x.  1 test test 1.9M Mar 26 14:16 main
-rw-r--r--.  1 test test     225 Mar 26 13:58 main.go
-rw-r--r--.  1 test test       9 Mar 26 14:07 test_file

[test@dmitry-desktop setguid]$ go build main.go 
[test@dmitry-desktop setguid]$ chmod u+s main
[test@dmitry-desktop setguid]$ chmod g+s main
[test@dmitry-desktop setguid]$ ls -la main
-rwsr-sr-x. 1 test test 1943252 Mar 26 14:18 main

[test@dmitry-desktop setguid]$ ./main 
Real UID: 1001
Effective UID: 1001
Real UID: 1001
Effective UID: 1001

now I run the same main from a different user

➜ whoami
dmitry

✗  /tmp/setguid/main     
Real UID: 1000
Effective UID: 1000
Real UID: 1000
Effective UID: 1000
panic: open /tmp/setguid/test_file: permission denied

goroutine 1 [running]:
main.check(...)
    /tmp/setguid/main.go:14
main.main()
    /tmp/setguid/main.go:32 +0xe5

main.go

package main

import (
    "os"
    "syscall"
    "log"
    "fmt"
    "time"
    
)

func check(e error) {
    if e != nil {
        panic(e)
    }
}

func main() {

    printdelay()

    errgid := syscall.Setuid(syscall.Getuid())
    if errgid != nil {
        log.Fatal(errgid)
        os.Exit(1)
    }

    printdelay()

    d1 := []byte("hello\ngo\n")
    err := os.WriteFile("/tmp/setguid/test_file", d1, 0644)
    check(err)
}

func printdelay() {
    fmt.Printf("Real UID: %d\n", syscall.Getuid())
    fmt.Printf("Effective UID: %d\n", syscall.Geteuid())
    time.Sleep(7 * time.Second)
}

then I tried the following:

sudo /sbin/setcap cap_setuid=ep main
chown root:root main
chmod +s main
ls -la main
-rwsr-sr-x. 1 root root 1943124 Mar 26 14:28 main

then I modify my go file to

// errgid := syscall.Setuid(syscall.Getuid())
   errgid := syscall.Setuid(1001)

and trying to invoke it:

✗  id
uid=1000(dmitry) gid=1000(dmitry) groups=1000(dmitry),10(wheel),969(plugdev),971(wireshark),975(docker) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c102

➜ ./main        
Real UID: 1000
Effective UID: 1000
2023/03/26 14:33:04 operation not permitted

what am I missing?

DmitrySemenov
  • 9,204
  • 15
  • 76
  • 121
  • 1
    What does `findmnt -v -T .` output? Does it say `nosuid` ? `nosuid` means setuid and file capabilities are not honored by the kernel for this mounted filesystem. – Tinkerer Mar 28 '23 at 01:42
  • It looks like your binary might be in `/tmp/`. In this case, try `findmnt -v -T /tmp`. On my system that definitely returns `nosuid`. – Tinkerer Mar 28 '23 at 02:45

2 Answers2

1

then I tried the following:

sudo /sbin/setcap cap_setuid=ep main
chown root:root main
chmod +s main
ls -la main
-rwsr-sr-x. 1 root root 1943124 Mar 26 14:28 main

then I modify my go file to

// errgid := syscall.Setuid(syscall.Getuid())
   errgid := syscall.Setuid(1001)

and trying to invoke it:

You do not mention whether you have rebuilt main after modifying the source code. I believe you have. Please note that the owner and file capabilities of main could be changed after the rebuild. I also believe that you have run the setcap and chown commands after the rebuild. Please edit the question to list the real steps.

So I think the issue here is that the command chown will change the file capabilities. You should run setcap after chown. See the commands and the output below:

$ getcap main  # at first, no file capabilities
$ sudo setcap cap_setuid=ep main
$ getcap main  # now the file capability "cap_setuid=ep" is added
main cap_setuid=ep
$ sudo chown root:root main
$ getcap main  # <== no file capabilities after "chown"
$ ./main
Real UID: 1000
Effective UID: 1000
2023/03/27 16:43:58 operation not permitted
$ sudo setcap cap_setuid=ep main
$ getcap main  # bring back the file capability
main cap_setuid=ep
$ ./main
Real UID: 1000
Effective UID: 1000
Real UID: 1001
Effective UID: 1001
Zeke Lu
  • 6,349
  • 1
  • 17
  • 23
  • `✗ getcap main` is `main cap_setuid=ep` and owner of the main is root:root with permissions `-rwsr-sr-x.` and I'm still getting operation not permitted. – DmitrySemenov Mar 27 '23 at 17:24
  • I found that the UID 1000 on my system is in the sudo group, while yours is not. Can you add it to the sudo group (run `sudo usermod -aG sudo dmitry`) and try again? – Zeke Lu Mar 28 '23 at 01:43
0

First, before you try anything, you have to be sure the place you are storing your main binary is on a filesystem that supports it running with privilege. Let's say, you want to store it in /tmp/, what mount options cover that directory?

$ findmnt -v -T /tmp
TARGET SOURCE FSTYPE OPTIONS
/tmp   tmpfs  tmpfs  rw,nosuid,nodev,seclabel,size=32756364k,nr_inodes=1048576,in

The option nosuid here means that even though you can change the privilege bits on the file (setuid or capabilities) under that directory, they will not be honored by the kernel when the program is run. Based on the details in your question, I think this is likely to be the problem you are experiencing.

Further, setuid-root on a program completely overrides file capabilities. So your attempts to add privilege to your binary are redundant. The only privilege it will get is from the setuid-root part.

If you find a directory that you can write to that is mounted without the nosuid option, you can use that. If all else fails: sudo mkdir /hack ; sudo chmod go+w /hack ; findmnt -v -T /hack will provide something you can clean up later.

In this directory, you can copy your binary, then pick one of the following to configure it:

$ cd /hack
$ sudo /sbin/setcap cap_setuid=ep main
$ /sbin/getcap main
main cap_setuid=ep

Or do this:

$ cd /hack
$ sudo chown root.root main
$ sudo chmod +s main
$ ls -l main
-rwsr-sr-x. 1 root root 1943124 Mar 26 14:28 main
Tinkerer
  • 865
  • 7
  • 9