in reply to Re: Not understanding the code to drop privileges in perlsec
in thread Not understanding the code to drop privileges in perlsec

I have read somewhere that we always should change $EGID before $EUID

Can you track down where you read that?

It isn't obvious to me that this is a bug in perl: in the list-assignment case, we drop privileges (by setting a non-root EUID) before trying to set EGID; it does not seem unreasonable to me that the attempt to set EGID then fails.

However I can also see the argument (particularly if you can find the above reference) that for list assignment perl should choose to modify groups before users, either always or only in specific (documented) cases.

And certainly if perl is correct to treat this the way it does, then it is wrong to recommend the code it does in perlsec.

Sadly I cannot readily test any of this, since on my Ubuntu system the set[ug]id bits are ignored for scripts. I do think it is worth opening an issue on github for this specific aspect of the perlsec code.

Replies are listed 'Best First'.
Re^3: Not understanding the code to drop privileges in perlsec
by Nocturnus (Scribe) on Feb 24, 2024 at 20:10 UTC

    Can you track down where you read that?

    I can't remember for sure, but it was eventually here. There is a comment in the middle of his code that states this. I believe it is not a constraint that's officially documented in an API; rather, it is logical, because we can't change the GID if we are already a non-privileged user (that is, if we have already changed our EUID away from root or from the suid-user, respectively).

    I agree with you that this probably is not a bug Perl, but it's a bug in the script, which, after all, is probably used by a lot of people because it claims to show how to securely drop privileges, preventing them from coding own solutions because nobody wants to have errors in security related code.

    in the list-assignment case, we drop privileges (by setting a non-root EUID) before trying to set EGID; it does not seem unreasonable to me that the attempt to set EGID then fails.

    This is what I initially thought, too. But as mentioned above, I have reversed the order of $EUID and $EGID in the two list context assignments at the begin and the end of the script for testing. This didn't change anything. I can't explain this, because I would have expected that list assignments go from the left to the right on both sides, and thus that the group id assignment would happen before the user id assignment after that change.

    Perhaps Perl tries to be overly smart; in every case, for whatever reason, it insists on changing the user id first regardless of the variable order in the list. Anyway, I am not that deep in Perl, so I can't tell whether it guarantees a certain order of single assignments in a list assignment at all. To make it work as expected, I had to write the two assignments each on its own in scalar context in the correct order.

    Sadly I cannot readily test any of this, since on my Ubuntu system the setugid bits are ignored for scripts.

    In case you are interested, I describe how I test it. Actually, I was in the same situation as you: Debian bullseye (or the interpreters, respectively) also ignores those bits on scripts. Plus, most of the suid-wrappers contain bugs and don't work as expected as well.

    However, I have found that one of those wrappers works like a charm: suid-wrapper. It is also available as a snap (Ubuntu can handle snaps, correct?), but downloading the source and compiling it really was a matter of two minutes, and it worked out of the box. No annoying study of man pages ... just copy the example command from the Github page and go on.

    In case of a Perl script without command line parameters (like our example script), it even simpler. Just execute sudo suid-wrapper --output root_pl test.pl This generates the stub root_pl, which is an executable that runs test.pl. The set[u|g]id flags on the script test.pl itself are still ignored, but you can change the owner of the stub executable root_pl to whatever user and group you want and then set the suid and sgid flags as you like.

    Furthermore, you can alter the actual script test.pl afterwards without generating a new stub, as long as you don't change its name of course. This is extremely nice.

    As a final remark, suid-wrapper by default sets the stub it generates suid-root and setgid-root, but as mentioned above, you can change the owner and group as you like. Just remember to make such changes to the stub, not the script. And please remember that Ubuntu probably resets suid and sgid flags on the stub if you change its owner or group. At least, this happens on Debian (and has driven me mad because I didn't notice it at once). It is a safety mechanism I guess.

      I have reversed the order of $EUID and $EGID in the two list context assignments at the begin and the end of the script for testing. [...] Perhaps Perl tries to be overly smart

      Yes, this is what happens in the special-cased handling of list assignment at pp_hot.c:S_aassign_uid that I mentioned earlier: assignments to any of the four variables are deferred until the reset of the list assignment is complete, then they are explicitly assigned ($UID and $EUID) first, ($GID and $EGID) next. This is the aspect that one could most plausibly argue is buggy.

      The bit that troubles me is that it is presumably not safe to assume that every such list assignment is aimed at dropping privileges; and in a world with ACLs, it maybe also be tricky to determine whether that's the intent. (The gist you link to, on the other hand, is specifically aimed at dropping privileges. In that context, "must setgid() before setuid()" makes perfect sense.)

      ... suid_wrapper ...

      Thanks, I'll take a look at that: I've hand-crafted such suid wrappers in the past, but not in the last decade or two. If I get that far before you do, I'll write up an issue around this.

Re^3: Not understanding the code to drop privileges in perlsec
by hv (Prior) on Feb 24, 2024 at 00:20 UTC

    Oops, I didn't intend to be logged out when I posted that.