The Use Case
I recently found myself in need of a way to escalate privileges for only one user on my system (me), for the purpose of running only one script without prompting for a password. It made sense to run the script setuid and have the code enforce the policy that I alone am allowed to run it. Using sudo in a way that limits permissions to the use of a given single command is one way to do it, but I saw this as a challenge. I had a perl script and I wanted to run it suid. I wanted to know if it could be done. And I proved it could, though it wasn’t easy at first.
Running suid isn’t an option that’s available to dynamic languages that run from source with a JIT interpreter. Shell scripts, Perl, Python… this type of code can’t run suid on its own. Only a fully compiled native binary file that doesn’t depend on an external interpreter can do so. To make it work, your source code would have to become a binary executable. Can you even do that?
How To Make It Work
Yes. You can accomplish this by using a patched App::Par::Packer (which provides the `
pp` command). As mentioned above, you must make it safer by enforcing privilege escalation limitations from within the source code, and encrypting the binary so that the source isn’t leaked (the encryption being the less-important, although more nifty of the two protections). It’s necessary to patch the source for App::Par::Packer so that it will allow you to run its compiled binaries with suid permissions. By default it does not allow this. Don’t send me hate mail for showing you how.
Now I originally started this writeup with the intention of showing you how to do all the steps. It turns out– that’s a pain, it’s error prone, and has some annoying limitations that I won’t catalog here. Instead, I just created a pre-packaged version of the patched source code and put it up on github in a tarball with examples and helper scripts. Happy birthday/Merry Christmas/whatever:
Now Go Get App::Par::Packer and Patch It
It’s actually really easy, because I already did it for you. Full instructions are provided in the git repository by following the steps in README.md which you really should read because it explains the basic prerequisite things you need to have and do before using this code to start making suid binaries.
You don’t need a gui for this. Either clone or download the source code onto your Linux machine, and begin by reading the documentation. If you don’t have/don’t want to use git, download the repo as a zip file at https://github.com/tommybutler/pp-suid/archive/master.zip
$ git clone https://github.com/tommybutler/pp-suid.git
Some Assembly Required
You can use the example Perl script (and helper shell scripts) as an example of how you might do this with your own Perl programs. As is mentioned in the aforementioned glorious readme, if you need more help using this patched software than is already given, you might not yet understand system security and secure coding well enough to be using it at all. Educate yourself first about running things with suid permissions.
One More Thing
At the time of writing, the current version of pp is 1.015, so that’s the version I patched. Eventually this will almost certainly fall out of date. You should be able to use the patched version for quite some time, however if you want a newer version of pp in the future and one gets released, you’ll need to patch that yourself.
If you do need to patch it yourself, take a look at what my patched code does and just modify the pp source files to do what it does: remove the check for directory ownership and all replace all instances of file creation with mode 0700 to mode 0777 — the resulting binaries will use that MASK and the resulting perms should be 0755 directories and 0644 files (NOT 0777). If you’re nervous about that, you can test it out for yourself and adjust as necessary.
I Don’t Always Run SUID
But when I do, I do it safely. Stay safe my friends.