Find the exploit…

Two of these can permanently drop the UID too 1000. The other two however results in a state where a previous privileged state can be restored. Can you spot which ones are right and which ones not? And most importantly: why?

$< = 1000;
$> = 1000;

$> = 1000;
$< = 1000;

$< = $> = 1000

$> = $< = 1000


Changing $< ($UID) to 1000 first will allow $< ($UID) to be changed back later.

This allows UID 0 to become 1000 and revert back to 0 but only if you reverse the order of the second set of assignments:

print "uid: $<\n";
print "euid: $>\n";
$< = 1000; # Set UID first followed by...
$> = 1000; # EUID if you want to change back later
print "uid: $<\n";
print "euid: $>\n";
$> = 0; # Change EUID first so that you have permission to change...
$< = 0; # UID back now
print "uid: $<\n";
print "euid: $>\n";

Same principle with the compacted assignments: print "uid: $<\n"; print "euid: $>\n"; $> = $< = 1000; print "uid: $<\n"; print "euid: $>\n"; $< = $> = 0; print "uid: $<\n"; print "euid: $>\n";

So in summary, to effectively change the UID for the life of a process, change the effective UID first.

(IMO, it makes perfect sense if you think it through.)

The opposite order is the one that would make sense, yes?

POSIX::setuid and be done with it.

The order to putting it back is the one that makes sense to me. Sorry about that, I should have been clearer.

Somehow I missed answering the part about why. The following answer assumes Linux (but probably works in a similar fashion in most *nix environments).

Since Perl is doing a system call to change the UID and/or effective UID, the kernel does the work. When it tries to change them, the kernel uses setreuid which takes two arguments: the requested UID and the requested EUID. (Setting either value to -1 if you want it left alone.) setreuid checks the UID and EUID before allowing either of them to be changed. UID 0 can change either to anything. Unprivileged users may change the EUID to be the same as the UID or back to the saved UID. If the UID is set or the EUID is set to something other than the previous UID, the saved UID will be set to the new EUID.

If you simply swap UID/EUID, it's easy to get back. When you want the process to be able to revert, you must only change the EUID or change the UID first and then the EUID (like I did in my example script, this sets the saved UID to 0). When you want the process to keep the new UID and EUID, you must change the EUID followed by the UID (you hit the special circumstance where the saved UID is changed to the new EUID).

[I think that I've described everything correctly enough. It is late here so maybe not exactly but close enough for others to understand I hope.]

After describing the process in detail, it all makes sense to me again and not just the order required to put the UID and EUID back as they were.

Leave a comment

About Leon Timmermans