Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

suid Perl with App::PAR::Packer (pp)

by Tommy (Chaplain)
on Oct 13, 2013 at 00:54 UTC ( #1058025=perlquestion: print w/replies, xml ) Need Help??
Tommy has asked for the wisdom of the Perl Monks concerning the following question:

SUID Perl?

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".

The Challenge

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 Details

Certainly 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.

The Problem

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!

strace my_suid_script

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.

#!/usr/bin/env perl use 5.017; # I have a tasty perlbrew use strict; use warnings; say 'Hello world'; exit;

The build procedure

#!/bin/bash sudo rm -rf /tmp/par* && sudo rm -rf suid_script && pp --clean -o suid_script && sudo chown root:root suid_script && sudo chmod +x suid_script && sudo chmod u+s suid_script && stat suid_script && rm -rf /tmp/par*

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

Replies are listed 'Best First'.
Re: suid Perl with App::PAR::Packer (pp)
by Tommy (Chaplain) on Oct 13, 2013 at 00:57 UTC


    What if I changed that to 0777...

    185 if (mkdir(top_tmpdir, 0700) == -1 && errno != EEXIST) {

    Hmmm... nope. Sefault. Still getting permission-denied. More fun... strace says:

    ... stat("/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=4096, ...}) = 0 access("/tmp", W_OK) = 0 mkdir("/tmp/par-746f6d6d79", 0777) = -1 EEXIST (File exists) getpid() = 3292 mkdir("/tmp/par-746f6d6d79/temp-3292", 0777) = -1 EACCES (Permission d +enied) mkdir("/tmp/par-746f6d6d79/temp-3292", 0777) = -1 EACCES (Permission d +enied) write(2, "./suid_script: creation of priva"..., 103./suid_script: crea +tion of private cache subdirectory /tmp/par-746f6d6d79/temp-3292 fail +ed (errno= 13) ) = 103 exit_group(255) = ?
    A mistake can be valuable or costly, depending on how faithfully you pursue correction

      Well. I got it to work by slightly changing the build process and by further alterations to the C source files of the App::PAR::Packer distribution. Since the changes were across multiple files and I can't exactly zip them up and share them here, please just contact me using this link and I'll give you the installable source and instructions if you're interested.

      Victory is mine! (And yours too, if you want a copy)

      Aside: I wonder why nobody front-paged this after such an elaborate write-up?

      A mistake can be valuable or costly, depending on how faithfully you pursue correction

        Since the changes were across multiple files and I can't exactly zip them up and share them here,

        Do a make realclean in respective directories and then  diff -ruN orig-pars-dists new-pars-dists

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1058025]
Approved by Athanasius
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (2)
As of 2018-05-27 18:33 GMT
Find Nodes?
    Voting Booth?