In asking this question, I'm not looking to be told that I shouldn't be doing this or that it's insecure. Those matters beyond the scope of this question.
Linux respects the setuid
andsetgid
bits on scripts when they are registered in the binfmt_misc filesystem with the credentials flag (C
). This is a demonstration of just that. I'm attempting to use this same facility to make shell scripts, specifically bash scripts, that will work with setuid
/setgid
. To this end, I crafted a binfmt string that employs file extension matching:
:pbash:E::pbash::/bin/bash:C
To test this, a file named test.pbash
that lacks a shebang line, is owned by root, and has the mode 6755:
echo "Effective UID: $(id -u)"
echo "Real UID: $(id -ru)"
Running test.pbash
outputs this:
Effective UID: 1000
Real UID: 1000
The effective UID should be displaying 0, so I had to search the web about bash
and effective UIDs. I found that bash
will change the effective UID to the real UID unless it is run with the -p
flag set. This requires that I change the binfmt and create a wrapper script.
The wrapper script, /bin/pbash
, with several invocations of -p
just in case:
#!/bin/bash -p
set -p
exec bash -p -- "$@"
Delete the old binfmt and load the new new binfmt string:
:pbash:E::pbash::/bin/pbash:C
Run test.pbash
and I get:
Effective UID: 1000
Real UID: 1000
Still not working. I figured I'd rewrite /bin/pbash
to output some information:
#!/bin/bash -p
set -p
echo "Executing '$0' '$1'" > /tmp/log.txt
exec bash -p -- "$@"
I run test.pbash
again and /tmp/log.txt
doesn't exist, as if /bin/pbash
didn't run at all. I remove the C
flag from the binfmt string, run test.pbash
again, and get the same output. What's changed is that /tmp/log.txt
exists! It says:
Executing '/bin/pbash' '/bin/test.pbash'
So my wrapper script isn't even running when I have the credentials flag set. Why is bash
or Linux behaving like this? How do I coerce it to run the wrapper script?