|There's more than one way to do things|
suid Perl with App::PAR::Packer (pp)by Tommy (Chaplain)
|on Oct 13, 2013 at 00:54 UTC||Need Help??|
Tommy has asked for the
wisdom of the Perl Monks concerning the following question:
So it turns out you can do suid perl scripts without suid Perl installed. Sort of.
The "Sort of" part of the above statement is what leads me to seek the wisdom of the Perl Monks yet again. I want to change the "Sort of" into a "That's right".
I got the idea yesterday that I'd like to have an suid Perl script, because, because. It's to run on my Linux machine, and I'm the only user. Yes, I'm aware of sudo. Yes I'm aware of security implications in a multi-user environment or on an enterprise server. However this is my machine and I want to do it, just to see if it can be done.
Pre-requisite DetailsCertainly it's not going to work in a traditional sense, because the Perl interpreter is not suid. However if you bundle your own Perl interpreter into a PAR executable via App::PAR::Packer, you can almost make it work by chmod()'ing u+s the resulting executable. As you may already know, App::PAR::Packer ships with the "pp" command. That's what you use to generate the binary executable, which is ridiculously convenient. Your entire app, it's Perl modules, and all their dependencies get zipped up and packed into an executable. You can then execute it like any other binary compiled for your system. When it runs, it unzips its payload of Perl modules and other bits into a pseudo-randomly named directory under /tmp.
In order to make sure users on a system don't accidentally run PAR apps that unzip to the same location and accidentally wipe out one another's PAR temp files, the executable creates its temporary directory with permissions of 0770, and then immediately checks if it is owned by $<. If it is not, the executable immediately bails with a message like "./suid_script: private subdirectory /tmp/par-746f6d6d79 is unsafe (please remove it and retry your operation)". So you get it: unless the temp directory is owned by the real UID running the script (not the assumed ID of root in an suid scenario), your PAR app takes its marbles and goes home. Not fun, but indeed a good security practice by the author of the code, I must admit.
My feeble attempt
Still, this leaves me with a problem. I thought I would try to take things into my own hands and I patched $DIST/myldr/mktmpdir.c of the module. Patched is a glorified word for "I commented out the if statement in the C code that does the ownership check". I then rebuilt and reinstalled the distribution. I recompiled the PAR executable -- now without the restriction in place, and ran it. Segfault. NOOOOO!
Oh look! If you watch the executable in action with strace, you can see that it segfaults for a good reason, even if I don't like it. With the ownership check removed in mktmpdir.c, my PAR executable runs...right into a wall. The compiled script which has been chown()'ed to root:root and chmod()'ed u+s, runs in suid mode as expected. The aforementioned pseudo-random temp directory is created with root ownership and permissions of 0700. But somewhere in the inner-workings of things unseen, for reasons I don't understand (yet), my PAR suid executable tries to extract its payload into the temp directory as $<. Naturally, that's never going to work, because user "tommy" can't write to root's mode 700 directory. And that's where head starts hitting the desk. I don't think I can outfox this one without help. So, do any ideas come to mind? (Smiling nervously).
My Perl code
This doesn't really make a difference. The problem happens in the PAR executable before this code ever runs.
The build procedure
Hopefully someone out there has some idea of what I can try next. In the mean time I'm going to try some more "patching"...
A mistake can be valuable or costly, depending on how faithfully you pursue correction