Beware of wilcards paths in sudo commands
Say you want to allow a non-root user on Linux to execute a couple of scripts as root or another user with more privileges. A common way of doing this is to make an entry in the sudoers file.
If the scripts are written in Python, it could look something like this:
johndoe ALL=(ALL) /usr/bin/python3 /opt/utils/*.py
Essentially, this means that the user johndoe
can execute /usr/bin/python3 /opt/utils/*.py
on any machine (ALL
) as any user ((ALL)
).
If the scripts in /opt/utils/
are not writable for johndoe
and there is no way for him to create new files in that directory, this looks okay at first.
What could go wrong?
The problem, though, is that if johndoe
can write anywhere else (which is always the case), he can easily get a root shell (or do literally anything else) due to the wildcard character in the path argument to python3
.
For example via a simple path traversal:
sudo /usr/bin/python3 /opt/utils/../../home/johndoe/gotcha.py
*
matches any character (including whitespace, as we see further below), so a user is free to modify the path as desired.
So while the wildcard sounds like an easy way to solve the initial problem, it is always better to be explicit about what a user can do (either by putting a restricting regular expression in place or – even better – always explicitly targeting a file or declaring a static argument), for example by listing all the full commands with the different arguments that are allowed to be executed or by building a small wrapper program.
Not a problem specific to interpreted scripts
This is not solely a problem related to executing scripts with an interpreter (like the above example) but applies in general.
The sudo
documentation presents another example using cat
:
%operator ALL = /bin/cat /var/log/messages*
Here, users in the operator
group are supposed to cat
only messages files. But simply adding a second argument using whitespace (which is covered by *
) would allow them to read any file:
sudo cat /var/log/messages /etc/shadow
The suggested safer alternative from the documentation is:
%operator ALL = /bin/cat ^/var/log/messages[^[:space:]]*$
This works fine when your path points to a file (which is expected when targeting /var/log/messages
). If messages
were a directory and not a file, you would have the same issue as above, though.
Conclusion: always be explicit in your sudoers
file about what you want to allow.
Like to comment? Feel free to send me an email or reach out on Twitter.
Did this or another article help you? If you like and can afford it, you can buy me a coffee (3 EUR) ☕️ to support me in writing more posts. In case you would like to contribute more or I helped you directly via email or coding/troubleshooting session, you can opt to give a higher amount through the following links or adjust the quantity: 50 EUR, 100 EUR, 500 EUR. All links redirect to Stripe.