Allow bash script to be run as root, but not sudo
Solution 1
The only way I could think of is to check one of the SUDO_*
environment
variables set by sudo:
#!/usr/bin/env sh
if [ "$(id -u)" -eq 0 ]
then
if [ -n "$SUDO_USER" ]
then
printf "This script has to run as root (not sudo)\n" >&2
exit 1
fi
printf "OK, script run as root (not sudo)\n"
else
printf "This script has to run as root\n" >&2
exit 1
fi
Notice that of course this solution is not future proof as you cannot stop anyone from setting a variable before running the script:
$ su
Password:
# SUDO_USER=whatever ./root.sh
This script has to run as root (not sudo)
# ./root.sh
OK, script run as root (not sudo)
Solution 2
Another option would be to check if the grandparent process name is "sudo":
#!/bin/sh
if [ "$(id -u)" -eq 0 ]
then
if [ $(ps -o comm= -p $(ps -o ppid= -p $$)) = "sudo" ]
then
echo Running under sudo
else
echo Running as root and not via sudo
fi
else
echo Not running as root
fi
Solution 3
The information about which user logged in is available in /proc/self/loginuid
. EDIT due to comments: That file does not seem to exist on all systems. I tested and it is available on Centos 6, Fedora 32, Fedora 33 and Ubuntu 20.04, all in standard x86_64 setups.
If we login as our user and than use sudo
or su
to become root, this will not change /proc/self/loginuid
and it will be some non-zero value. If we directly log in as root, then cat /proc/self/loginuid
will return 0
. Note that this file can NOT be modified, even root cannot do this. EDIT due to Stéphane Chazelas' comment: Root can overwrite this file using echo 0 > /proc/self/loginuid
. However, this can be prevented by setting auditctl --loginuid-immutable
.
The script to check for real root (if auditctl --loginuid-immutable
is set) could look like
#!/bin/bash
loginuid=$(cat /proc/self/loginuid)
echo $loginuid
if [[ $loginuid -ne 0 ]]; then
echo "You did not log in as root."
exit
fi
Solution 4
Avoid sudo
in root
bash script?
Preamble: Care sharing root account!!
Unfortunely, there is no resistant way... Please read carefuly upto last paragraph
Once you give root access to someone, they could do anything, including editing your script!!
For sample, if user hit sudo su -
, then variables SUDO_*
doesn't exist anymore...
First quick way using pstree
So simplier way to search for sudo
presence in whole current tree, seem to use pstree
:
die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
pstree -s $$ | grep -q '\bsudo\b' && die "Can't be run under sudo"
With ps
only, you could loop over ps ho ppid
:
die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
pid=$$
while read pid name foo < <(ps ho ppid,cmd $pid) && ((pid>1));do
[ "$name" = "sudo" ] && die "Can't be run under sudo"
done
Regarding comment about renamed sudo
If sudo command is renamed or copied, then instead of looking for command name, look for UID in whole parent tree. So script is same than previous, but searching for UID >= 1000
in parent tree:
die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
pid=$$
while read pid uid < <(ps ho ppid,uid $pid) && ((pid>1));do
((uid>999)) && die "Can't be run under sudo"
done
Because we are speaking about Un*x
To be correct, avoid using fixed statical datas, use of UID_MIN
from /etc/login.defs
:
die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
while read fld val;do
case $fld in UID_MIN ) UIDMIN=$val ;break ;; esac
done </etc/login.defs
((UIDMIN)) || die Getting UID_MIN.
pid=$$
while read pid uid < <(ps ho ppid,uid $pid) && ((pid>1));do
(( uid >= UIDMIN )) && die "Can't be run under sudo"
done
Workaround for executing this by using sudo
anyway
But all this is someting fragile:
$ sudo su -
# screen -D -R # apt install screen if not installed
Now hit Ctrl + a , then d to be detached. Type exit
or hit Ctrl + d to return in user mode...
Then simply:
$ sudo screen -x
Now, you'll be logged in a root login session. No trace of any sudo.
# ps $PPID
PID TTY STAT TIME COMMAND
26367 ? Ss 0:00 SCREEN -D -R
# ps ho ppid $PPID
1
# set | grep SUDO
# <-- nothing here!
Conclusion
As chepner rightly commented: sudo is drawn to give specifics access to specifics tools:
Nothing about sudo requires it to give you root access; that's just the default behavior everyone is familiar with. sudo can be configured to allow you to do only very specific things, including not gain root access at all – chepner
Care to configure them correctly, before using fragile workaround!
See:
apropos sudo
And read carefully
man sudo.conf
man sudoers
Regarding logname
Have a look at correct Stéphane Chazelas's answer! This could be the best answer for a homework!!
Again, lot of workaround, like: echo 0 > /proc/self/loginuid
...
About /proc/self/loginuid
under Linux
Please read interesting laolux's answer about this!
die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
read lUid </proc/self/loginuid || die "Can't access procfile"
((lUid)) && die "You must be logged as root."
( This syntax avoid forks! )
But anyway
- script could be copied and edited
- depending on config/kernel, this kernel entry could be spoofed
-
Sudoer could create
cron
entry for initiating specialscreen
session asroot
. (cron
andscreen
are not the only way for doing things like this! Just the first coming to my mind. )
Solution 5
I suggest checking out process list strings and see if the user is running the program using sudo
contype=`tty | cut -d '/' -f 3`
tty="$contype/`tty | cut -d '/' -f 4`"
if [ "$(id -u)" -eq 0 ]
then
res=`ps ax | grep "$tty" | grep "$0" | grep "sudo"`
if [ $? == 0 ]
then
echo "You should not run the script using sudo!"
exit 2
else
echo "Done."
fi
else
echo "You are not root. Run this script as root."
exit 2
fi
That variable res
is simply for you if you want to filter results again.
Arkadusz code is nice but unfortunately it can be bypassed really easy...
PK001
Updated on September 18, 2022Comments
-
PK001 over 1 year
I'm new here and new to bash/linux.
My teacher gave me an assignment to allow a script to be run only when you're "really" root and not when you're using sudo. After two hours of searching and trying I'm beginning to think he's trolling me. Allowing only root is easy, but how do I exclude users that run it with sudo?
This is what I have:
if [[ $EUID -ne 0 ]]; then echo "You must be root to run this script." exit fi
-
Markus over 3 yearstry this:
sudo whoami
... I'm not sure that the problem is defined particularly clearly.sudo whoami
claims you're root. Even if that doesn't count, what if you run a shell with sudo, does that count as using sudo? -
mikem over 3 yearsTake away sudo all and enumerate only the commands a user should be allowed to run as root, excluding your script from that list.
-
Kusalananda over 3 yearsHow would you stop the
sudo
user from removing the restriction from the script once you have put it in place? There is no difference between the root user logged in from a console and the root user accessing the system viasudo
. The foolproof solution woud be to simply uninstallsudo
. -
Mark Morgan Lloyd over 3 yearsI think that any answer has to consider the possibility that the lecturer is ignoring (or hasn't thought of) the possibility of running sudo su and intends that the solution prevents the script from starting to run rather than aborting if it doesn't like its execution environment. As such I agree with @mikem and would highlight man sudoers -> SECURITY NOTES since some of their caveats aren't relevant if the script can't be edited or renamed.
-
Olivier Dulac over 3 yearsyour best bet is to restrict users to not be able to execute the script via sudo, using answer superuser.com/a/735286/174998
-
Kusalananda over 3 yearsIs the root user to be allowed to execute the script through
sudo
? -
Andrew Henle over 3 yearsOn any secure system, there are no shared accounts. There will be no such thing as "really" root - the actual user will always be known. Limitations like this are worse than useless - they give you a false sense of security when all they really do is maybe slow down malicious actors for a few seconds. You'll think you're secure while in reality you're being pwned by every half-wit with an internet connection who can spell "Google".
-
chepner over 3 yearsNothing about
sudo
requires it to give you root access; that's just the default behavior everyone is familiar with.sudo
can be configured to allow you to do only very specific things, including not gain root access at all. -
Joshua over 3 years@JaredSmith: He probably wants an anti-bonehead solution not a secure solution. Else sudo-user could copy the script and take the check out.
-
F. Hauri over 3 years@MarkMorganLloyd By running
sudo su -
, thanscreen
detach an re-attach, You'll be in a root login shell. See my answer -
F. Hauri over 3 years@chepner I've cited your comment in my answer
-
Mark Morgan Lloyd over 3 years@FHauri I know you can get a shell, and so apparently do you. But as I explicitly said: the lecturer might not, or might be attempting to not ask that question.
-
-
pLumo over 3 years+1, but this can still be tricked by something like
sudo bash -c 'unset SUDO_USER=; my_command;'
. I would not rely on it. -
PK001 over 3 yearsIt might not be good for actual work, but it will probably get my assignment done, which is what matters at the moment. Thank you very much!
-
doneal24 over 3 yearsThis fails if the user is logged in on
tty/1
or another physical device. -
zbx0310 over 3 yearsThanks for alerting me. I've edited the code...
-
waltinator over 3 yearsThe paranoids among us would begin with
env | grep -E 'SUDO|PPID|PID
, and validate each via/usr/bin/ps
and other command line tools. One could check each ofenv | sort
, if one wished. -
F. Hauri over 3 yearsCare! If user run
sudo su -
, variablesSUDO_*
doesn't exist anymore! See unix.stackexchange.com/a/626764/27653 -
piojo over 3 yearsThis fails when run under "sudo su" or "sudo bash". I think you need to check multiple levels of processes.
-
Nick Kennedy over 3 yearsOn Raspberry Pi OS (which is a Debian-based OS), this file doesn’t seem to exist
-
Nick Kennedy over 3 yearsIn common with other solutions, this fails if the sudo command has been renamed
-
Jeff Schaller over 3 yearsGranted; you could extend this to loop over the PPID process tree, but I thought I'd cover the most common usage of
./script
orsudo ./script
, to point out the core idea of checking for "sudo" as being the parent process or not. -
laolux over 3 yearsThat's surprising. I checked Fedora 32, 33, Centos 6 and ubuntu 20.04 and they all have that file. I checked on x86_64 only, not on arm systems.
-
Radovan Garabík over 3 years@NickKennedy FWIW it does not exist on my ODROID U3 (armhf xenial) as well
-
Davis Yoshida over 3 yearsThis can be circumvented by sudoing then running a process that disowns right?
-
Jeff Schaller over 3 yearsThis answer attempts to address the question "how do I exclude users that run it with sudo?" in the straightforward interpretation, not as an exhaustive security measure. I don't know what the professor was aiming for, but maybe parent processes were on the syllabus and they thought this would be an instructive exercise. Any simple measure can be fooled, as we've seen so far with the straightforward SUDO_USER environment variable and these process checks.
-
Michael Homer over 3 yearsI believe this requires
CONFIG_AUDIT=y
, on Linux, but on such a system this is the right answer (though I imagine the actual goal was to read the manual and find SUDO_USER). -
Joshua over 3 yearsIf you would like to see a script that defeats
/proc/self/loginuid
I think I can provide. -
Joshua over 3 yearsUpdate: I know I can provide. It's possible to convince init to re-exec itself, which means it's possible to convince init to re-exec anything you darn well please.
-
F. Hauri over 3 yearsCare, if user run
screen
,. detach then re-attach, you can't see anything more than a root login shell!! See last paragraph at: unix.stackexchange.com/a/626764/27653 -
Stéphane Chazelas over 3 years
sudo sh -c 'echo 0 > /proc/self/loginuid && cat /proc/self/loginuid'
outputs 0 for me on Ubuntu 20.04 with a 5.4.0-58-generic Linux kernel -
Stéphane Chazelas over 3 yearsCan be changed with
auditctl --loginuid-immutable
. -
Wastrel over 3 years@Mark I knew someone would say that. The question doesn't specify that the script itself has to "decide" who can run it, just that root can run it but not a sudo user. It occurred to me that this was what his instructor was looking for. Maybe I'm wrong.
-
Joshua over 3 years@laolux: Although once I have root I can defeat it outright as I said earlier.
-
Joseph Sible-Reinstate Monica over 3 years
auditctl --loginuid-immutable
doesn't make this perfect either. In particular, it only prevents you from changing your loginuid once it's been set, but there are ways that you can gain control of, or start, a process where it never gets set at all. -
Joseph Sible-Reinstate Monica over 3 yearsBypassable by running
screen
inside of sudo -
laolux over 3 years@JosephSible-ReinstateMonica yes, but I could imagine restricting sudo to execute let's say bash only. That way it would automatically be set on your shell. However, I am not sure about inheritance rules, so there could be a loop.
-
laolux over 3 years@Joshua yes, on systemd it is very easy to re-exec init:
init u
. Unfortunately I do not know enough about systemd to know how to change the configuration, but I could imagine that changing the configuration may be restricted. For example, on Fedora kexec is outright blocked, so even root cannot do as it pleases. -
F. Hauri over 3 yearsPlease try this:
IFS=/ read foo{,} tty < <(tty)
, thenps --tty $tty fw
! Anyway, this fail if sudo is renamed, if user runsudo su -
, thenscreen
... -
F. Hauri over 3 yearsBest attended answer, I think! You've been cited in my answer