Broken security advisory (again)

merlyn on 2005-12-17T20:11:31

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}


Confusing

bart on 2005-12-17T22:13:58

I don't understand the technical background behind the variables $<, $> , $( and $).

Perhaps an idea for a column?

Or perhaps you did one already, and I just don't know about it.

My current solution is to simply stay away from writing or touching code that requires that knowledge. :)

Re:Confusing

Dom2 on 2005-12-17T22:23:14

There's some good background info in this paper.

-Dom

Dropping privileges in Perl

pjf on 2005-12-18T08:39:45

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.