As found in this advisory (as well as being picked up a few other dozen places on the net):
The security issue is caused due to the Perl binary included with Mac OS X not correctly dropping privileges when a Perl application uses the "$< = numeric_id;" statement to set its uid. The issue may be caused due to wrong compilation options being selected when compiling the Perl binary.
What are these nutheads smoking? That variable doesn't control effective UID. It controls real UID, which has nothing to do with the effective privileges.
How do people get that messed up? I dunno. Maybe we need better books. {grin}
$<
, $>
, $(
and $)
.Re:Confusing
Dom2 on 2005-12-17T22:23:14
There's some good background info in this paper.-Dom
The advisory itself is rather vague and unhelpful. I'm surprised it exists at all. However it does provide an excellent opportunity to talk about Unix privileges and Perl. Randal, I hope you don't mind me using your journal too much for this purpose.
Dropping privileges in Perl is notoriously hard, and stems primarily from the fact that most unix systems provide at least three flavours of uid (real, effective, and saved), whereas Perl provides only two (real and effective). The saved uid, the one you can't easily access from Perl, is the effective uid when the process started. It exists so one can switch between real and saved uids throughout the course of a program.
On many systems, the line:
$< = $> = $NEW_UID;
will permanently drop privileges to $NEW_UID, dropping the saved uid as well. However this may only happen if you're running as root. On most systems the lines:
$< = $NEW_UID;
$> = $NEW_UID;
will not permanently drop privileges, even though it does set the real and effective uids. One can successfully regain privileges later in the program.
Worse still, the values of $< and $> are cached, and may or may not actually have anything to do with the current process privileges. This makes it particularly difficult if you're relying upon calls out to C/XS to perform privilege manipulation.
Proc::UID is one attempt to try and provide sensible privilege semantics to Perl without the programmer needing to worry about the idiosyncrasies of each particular operating system. It makes available all three user-uids, without caching.
Unfortunately to make Proc::UID truly effective I do need to worry about all the idiosyncrasies, and also to find a few spare round tuits, which always seem in short supply. Patches, particularly for non-Linux/BSD systems, are especially welcome.
Re:Dropping privileges in Perl
jmason on 2006-01-04T22:22:53
Perl on MacOS (and possibly other BSDish platforms) *does* indeed have some unportable wierdness regarding uid/euid handing, as we found in this SpamAssassin bug report.
It appeared that some perl versions required RUID==EUID==0 before $ = 100; $" would silently fail to drop RUID==0 privs, and instead leave it at 0. To quote the bug report:
Interestingly, the same exact issue occurs on my Mac OS X machine,
but not any of the other platforms I have access to...
root# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $>=1000; p; $<=$>; p;'
RUID: 0, EUID: 0
RUID: 0, EUID: 1000
RUID: 0, EUID: 1000
The end line should read "RUID: 1000, EUID: 1000".
Linux:
# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $>=1000; p; $<=$>; p;'
RUID: 0, EUID: 0
RUID: 0, EUID: 1000
RUID: 1000, EUID: 1000
Solaris:
# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $>=1000; p; $<=$>; p;'
RUID: 0, EUID: 0
RUID: 0, EUID: 1000
RUID: 1000, EUID: 1000
More interesting bits:
# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $<=1000; p;'
RUID: 0, EUID: 0
RUID: 0, EUID: 0
# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; use POSIX; setuid(1000); p;'
RUID: 0, EUID: 0
RUID: 0, EUID: 0
# cat - > t.c
main() {
printf("RUID: %d, EUID: %d\n", getuid(), geteuid());
setuid(1000);
printf("RUID: %d, EUID: %d\n", getuid(), geteuid());
}
# gcc t.c
#./a.out
RUID: 0, EUID: 0
RUID: 1000, EUID: 1000
so setuid() obviously works. just not at all from perl.Our workaround was to check $< and $> after the first (POSIX-ish) idiom, then use
$> = $<; $< = 100; $> = "100 100";
This is the patch we applied to SpamAssassin to do this.Re:Dropping privileges in Perl
jmason on 2006-01-04T22:25:51
'It appeared that some perl versions required RUID==EUID==0 before $ = 100; $" would silently fail to drop RUID==0 privs, and instead leave it at 0.'
well, that made no sense. sorry; forgot to escape $< and $>. anyway, read the pasted code; it's all pretty clear there.