Table of Contents
- Introduction
- Setting the Stage
- Opening the Door
- Threats Lurking in the Hallways
- The File Upload Trap
- Peeking Behind the Curtain
- Climbing the Ladder
- Persistence in Plain Sight
- Reflections
- Conclusion
1. Introduction
There is a quiet intensity that settles in when you are tasked with breaking into a system, not out of malice, but to help in building a stronger security posture.
This was my first official penetration test, and I knew that it was not just about finding flaws, but about proving that I could think like an attacker, act with discipline, and deliver value to a real-world project.
The target was a cloud-hosted e-learning system meant to support virtual classrooms, user accounts, and administrative tools. It was structured like a system that any typical education-focused startup might build: functional, user-centric, and as I would soon find, deeply vulnerable in the places it mattered most.
I was not handed a red carpet of credentials to begin with, all I had with me was just an IP address, a scoped objective, and two weeks to simulate real attacker behavior. The job in question is to:
- Identify
- Exploit, and
- Document vulnerabilities across the internal network and web application
while sticking to ethical boundaries and leaving behind a cleaner, more secure system.
I did not know it at the time, but this engagement would take me through nearly every major step of a modern attack chain, from an overlooked FTP service to full root access via a writable cron job and cracked passwords. It would test more than my technical skillset. It would challenge my persistence, my instincts, and my attention to details.
This is not just a dry walkthrough but the story of how I breached a digital classroom, and the lessons I learned on the way in and out.
2. Setting the Stage
The platform looked harmless at first glance, a straightforward e-learning application designed to host courses, manage users, and support virtual interactions between students and instructors. But simplicity in appearance does not always mean simplicity in infrastructure, and for an attacker, it often means something more dangerous: a sense of safety.
From a technical perspective, the platform was deployed on a Debian-based virtual machine accessible via a private IP: 192.168.56.2. It ran a typical LAMP stack, hosting both the web application and various backend services including FTP, SSH, HTTP Service and phpMyAdmin. That combination alone raised a few red flags before a single packet had been captured.
The goal was to perform an internal black-box penetration test. That meant no privileged access, no back-end or source-code knowledge, just a scoped attack surface and permission to simulate real-world adversarial behavior. The objective was clear: identify misconfigurations, vulnerabilities, and logic flaws that could lead to unauthorized access or compromise sensitive data.
The project was framed within a two-week testing window, and everything had to be self-contained. Discovery, exploitation, privilege escalation, persistence, and reporting, no second chances, no stretch deadlines.
I had a plan:
- Reconnaissance and Enumeration (find open ports, exposed services, and hidden directories).
- Initial Access (exploit weak configurations or leaked credentials).
- Post-Access Exploration (move laterally through services and privilege boundaries).
- Persistence and Cleanup (show how an attacker could maintain control without being noisy).
- Documentation (back every action with evidence, screenshots, and a trail of thought).
This was not just a hunt for CVEs or a checklist-driven scan. It was an opportunity to think like a real attacker, not just where they would go, but why, and how quickly the gaps could stack into a full compromise.
And it did not take long for the first one to appear in a place many defenders still overlook.
3. Opening the Door
In penetration testing, there are few discoveries more quietly thrilling than stumbling upon a low-hanging fruit that turns out to be the key to everything. For this test, that key came early and quietly in the form of an open FTP service.
While scanning the network with nmap, I noticed port 21 was open. Not a major surprise; FTP is still very much around and in use. But what caught my attention was the anonymous login that succeeded on the first attempt, no credentials, no challenge, just an open door into the filesystem.
The directory wasn’t empty either. Sitting right there in plain view was a file called note.txt.
I have learned that files with innocent names often carry the heaviest consequences, and this one didn’t disappoint. Opening it revealed a hash, an MD5 digest with no salt and a note suggesting it belonged to a student account used for “testing SQL queries”.
In real-world terms, this was an accidental credential leak sitting on a public-facing, unauthenticated service.
The hash went straight into crackstation.net, and in seconds, I had the original password: a simple, guessable string.
Armed with this cracked password and the associated username, I navigated to the web portal and attempted a login. It worked on the first try.
This was not privilege escalation neither was it shell access but it was my first foothold authenticated access to the web interface, which meant access to functionality, endpoints, webpages and upload mechanisms intended for real users.
And just like that, the attack had begun, not with a zero-day vulnerability, but with a forgotten file on the FTP service.
4. Threat Lurking in the Hallways
Gaining access to the web portal with the cracked credentials did not unleash immediate chaos. What it gave me was more subtle, a student’s view of the system, and with it, access to web pages and features an outsider would not typically reach. For many attackers, this is a sweet spot: inside the network, but still below the radar.
But before I could even get there, I hit a wall.
Typing the Academy server’s IP into a browser did not lead to a login screen. It just showed the default Apache landing page, the kind that suggests something is running, but nothing is exposed yet. It was a dead end, at least on the surface.
That was when I turned to directory brute-forcing.
Using dirsearch with a large SecLists wordlist, I scanned the server for hidden or non-obvious routes. The results were revealing; /academy and /phpMyAdmin were the two directories that changed the scope of the test entirely.
/academywas where the actual e-learning platform lived./phpmyadminexposed the backend database interface directly.
With that discovery, I navigated to /academy on the browser, I was greeted by a login page and I entered the cracked student credentials from note.txt, this gave me authenticated access to the student portal.
Now I had a foothold.
From this new vantage point, I started quietly exploring, no payloads, no breakage, just observation.
The portal had a fairly common layout: user profiles, course access, password management with form fields. My first line of inquiry was to test for client-side vulnerabilities like Cross-Site Scripting (XSS) and SQL injection. I probed form inputs, tweaked query strings, tried reflection and basic bypasses. But the defenses held; either input was sanitized, or error responses were too generic to be useful.
With no client-side vulnerabilities to exploit, I shifted focus to functionality.
That was when I noticed the profile picture upload feature. A user-facing mechanism for updating avatars. These features are often rushed through development, sometimes because they seem low-risk, other times because they are considered too minor to warrant scrutiny. But in reality, they are one of the most frequently exploited entry points in web applications.
I uploaded a basic image, no issues. Then a .php file and boom, it was accepted. No warning, no restriction, no MIME check and directory listing was enabled.
I followed the image tag’s path to the uploads folder and found my test script sitting there, executable.
Clicking it? It ran instantly, no sandboxing, no renaming, no denial.
At that moment, the game changed.
I was not just lurking in the hallways anymore. I had found a side door to the server room and it was wide open.
5. The File Upload Trap
It is one thing to find a misconfigured upload feature, it is another to realize that it allows you to run your own code on the server, completely unchecked.
After confirming that .php files could be uploaded and executed, I crafted a more serious payload: a reverse shell, disguised as an image. The file extension was .jpg, but the contents were pure PHP, designed to initiate a connection back to my machine the moment it was accessed.
I fired up my listener using netcat, uploaded the file through the student profile panel, then navigated to the file’s public URL. Within seconds, my terminal lit up.
- Connection received.
- Shell established.
- User:
www-data.
This was it, remote code execution, quietly achieved through nothing more than a web form meant to change a profile picture. The web server had accepted the file, exposed it publicly via directory listing, and passed it directly to the PHP interpreter. No sanitization. No execution restrictions. No alerts.
Once inside, I needed stability. The initial shell was brittle, more like remote command injection than a real terminal. So I upgraded it by spawning a pseudo-terminal using Python:
python3 -c 'import pty; pty.spawn("/bin/bash")'
Now I had a better foothold, capable of navigating the system, listing users, and gathering intelligence. I began enumerating the environment checking services, permissions, cron jobs, anything that looked out of place or exploitable.
I had landed inside the network. The code upload door was behind me. The rest of the infrastructure lay ahead.
BUT I was still operating as a low-privilege user.
6. Peeking Behind the Curtain
With shell access as www-data, I had full access to the beating heart of the application. This is where attackers get quiet. No payloads, no exploits just enumerating, traversing paths, directories and reading juicy files, line by line, until something slips.
It did not take long.
Inside academy/includes/config.php, I found a hard-coded SQL credentials. Plain text, no encryption, no obfuscation, no environment variables.
Just:
$mysql_user = "grimmie";
$mysql_password = "My_V3ryS3cur3_P4ss";
This was not just a minor misstep. These credentials represented direct access to the underlying database, and they worked flawlessly when tested against the platform’s open phpMyAdmin interface.
Yes, that too was exposed. No IP restriction, no second layer of auth. Once I had the credentials, logging into phpMyAdmin was as easy as visiting a URL.
From there, the view widened dramatically. Inside the database I found user tables, hashes, and most critically the super admin account. The password was hashed, but weakly: unsalted MD5, just like the one in note.txt.
Crackstation made short work of it. Seconds later, I had super admin-level access inside the application.
To summarize:
- I uploaded a disguised PHP shell.
- Used it to access the full web app source code.
- Found SQL credentials hard-coded in a config file.
- Logged into phpMyAdmin.
- Cracked the super admin password from the database.
At this point, I had not even escalated privileges on the OS yet but I had already compromised the entire platform from a data perspective.
The next step will be all about pushing the compromise all the way to the system level and beyond.
7. Climbing the Ladder
After compromising the web application and database, I returned to my reverse shell session that was still running quietly as www-data. My goal now was to move laterally and escalate privileges at the system level.
I started with the a post-exploitation tool called LinPEAS.sh: checking for users, writable files, scheduled tasks, and accessible binaries. Enumeration revealed a user named grimmie, and more importantly, a file in their home directory: /home/grimmie/backup.sh.
What stood out was not just the file, it was how it was executed. A quick look at system processes and crontab revealed that backup.sh was being run by root every minute. But the file itself? Owned and writable by grimmie.
This was a textbook privilege escalation flaw: a non-root user controlling a script that is executed by root. The only catch? I was not grimmie (yet).
That was when I remembered the SQL password found earlier, the one found in config.php and used to access phpMyAdmin. Could it be reused again?
I ran su grimmie, entered the same password.
It worked.
No brute-force, no guesswork. Just another case of credential reuse and suddenly, I was an administrative user on the operating system, with write access to a root-executed script.
I replaced the contents of backup.sh with a reverse shell payload and set it as executable. Moments later, my listener picked up a new connection, this time from root.
Full control.
No privilege boundaries.
No alerts.
From a forgotten note accessible via FTP through anonymous login to full root shell, the entire chain had unfolded because of basic misconfigurations and password hygiene failures.
But I was not done yet, even with root access, I still had to rely on reverse shell to access the system. There must be a means to have a persistent access without the need to setup reverse shell for future connections.
8. Persistence in Plain Sight
By the time I gained root access, I had effectively won the engagement but a real attacker does not just celebrate, they dig in. And persistence is how they ensure that once they are in, they do not need to break in again.
The most quiet, durable, and undetectable way to maintain access? SSH key injection.
I generated an SSH key pair on my machine and copied the public key into /root/.ssh/authorized_keys. There were no access controls stopping me, no file integrity monitoring systems watching. I could now log in as root from anywhere, no passwords, no noise, no need to re-exploit anything.
This step was not just technical. It was philosophical.
It proved that once compromise is achieved, without monitoring and alerting, an attacker does not need to do anything noisy to maintain access. No malware, no command and control infrastructure, just a key in a file, and silence.
And to make matters worse, my enumeration with LinPEAS.sh had also identified two unpatched kernel vulnerabilities: CVE-2019–13272 and CVE-2021–22555. I did not even need them but someone with fewer morals and more time could have chained those too, in case the simpler paths ever got closed.
This was not just a successful compromise. It was a quiet, resilient takeover, made possible by forgotten files, poor password policies, and unmonitored privilege escalation paths.
And it all happened without tripping a single alarm.
9. Reflections
This engagement taught me that exploitation is rarely about sophistication, it is more about compounding weaknesses. No single issue on this platform was groundbreaking on its own. But together? They formed a clean, frictionless path from anonymous access to root control.
What stood out most was not the number of vulnerabilities, it was their alignment:
- An open FTP service that leaked credentials.
- Directory brute-forcing to show hidden or non-obvious routes.
- A profile upload feature that allowed remote code execution.
- Reused passwords across application database and system accounts.
- A writable cron job used by root.
- No detection, no alerts, no endpoint controls.
Each flaw enabled the next. Each oversight deepened the compromise.
What’s more, the technical failures reflected broader cultural issues common in growing environments:
- security as an afterthought,
- default configurations left unchanged, and
- lack of separation between development and production credentials.
For defenders, this test reinforces a few key truths:
- It only takes one overlooked misconfiguration to open the door.
- Password reuse is still one of the fastest paths to escalation.
- Attackers do not need privilege to cause damage, they need time and silence.
- Without internal monitoring, even root-level compromise can go unnoticed.
For me personally, this was not just a test of tools and tactics, it was a lesson in discipline, enumeration, and the value of thinking patiently like an adversary.
It proved that security is less about technology and more about what is left unchecked.
10. Conclusion
The platform was designed to educate but during this assessment, it taught a different kind of lesson.
Over two weeks, I moved from an anonymous FTP login to full root access. There were no exotic exploits, no bleeding-edge techniques. Everything used was based on well-documented weaknesses and avoidable misconfigurations including: poor password practices, exposed admin tools, improperly configured uploads, and unmonitored escalation paths.
Every step could have been blocked with basic defensive measures. Every breach was preventable.
What this test confirmed is something that security professionals often preach but rarely get to demonstrate so cleanly:
Security is not about stopping attackers from trying, it is about making sure they can not succeed quietly.
The platform offered a clear view of how small oversights can stack into systemic compromise but more importantly, it provided an opportunity to reinforce better practices and to showcase the value of proactive testing before a real adversary arrives.
This was my first official penetration test and it reaffirmed why this work matters.
Every vulnerability delayed, overlooked, or dismissed is just an attack waiting for its moment.
And as this experience shows:
Vulnerability delayed IS NOT vulnerability denied.
– Mastercraft ‘25