Sitemap
Ahmed Atef

Ahmed Atef

Bug Bounty Hunter && CTF Player

From Recon to RCE: Inside a Black-Box Penetration Test

13 min readAug 2, 2026

--

Press enter or click to view image in full size

Hello everyone! Today, I want to share how I approached a recent penetration test on an e-learning platform.

While this was only my third official client engagement, every assessment brings its own unique challenges. This one ended with Remote Code Execution (RCE).

In this post, I’m excited to share the mindset and techniques that helped me along the way. Of course, this isn’t the only methodology that works, but I hope sharing my workflow sparks a few new ideas for your own targets!

During this assessment, I used HuntBox.io — a cloud-based workspace I built for bug bounty hunters and security researchers — to map target assets, track findings, store requests, and keep my notes organized.

Setting the Rules of Engagement (RoE)

As the platform continued to grow and attract more users daily, the client wanted to ensure their security matched that growth. They requested a simulation of a real-world attack scenario: a pure black-box test with no provided credentials, designed to answer a single question — How far could an unprivileged attacker actually get?

To keep the assessment controlled and aligned with their goals, we defined a clear scope and rules of engagement:

  • In-Scope: The primary application domain and all associated subdomains (*.example.com).
  • Out-of-Scope: Actions that could cause service disruption or downtime, social engineering, and phishing.
  • User Safety: No interaction with real user data to preserve production integrity.
  • Critical Findings: Any critical vulnerabilities identified during the assessment were communicated immediately through the agreed reporting channels rather than waiting for the final report.

Phase 1: Passive Reconnaissance

The first phase focused on gathering as much information as possible without directly interacting with the target infrastructure. The objective was to answer three key questions:

  • What subdomains exist?
  • What technologies are in use?
  • Are any secrets or sensitive assets publicly exposed?

I started with standard OSINT techniques, using Google Dorking operators like site:example.com to see what search engines had indexed. I also searched GitHub for accidentally exposed API keys or tokens and checked the Wayback Machine to inspect historical versions of the application. There wasn’t much indexed, revealing only a few standard results.

However, checking VirusTotal’s domain relations revealed historical DNS records, associated IP addresses, and several potentially interesting subdomains, such as admin.example.com and admin-staging.example.com. I also used tools like subfinder to check for any additional results.

A quick WHOIS lookup on the root domain confirmed that privacy protection was enabled, keeping administrative contact details hidden, and the domain status was set to clientTransferProhibited (registrar lock).

Next, I moved to DNS enumeration using standard command-line tools:

dig example.com A

When querying the subdomains discovered earlier — even for non-existent hostnames — every query returned a valid response with an IP address:

;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

This confirmed the presence of a wildcard DNS record.
Instead of immediately assuming it was a misconfiguration, I took a step back and tried to understand it from a business logic perspective. I realized that the platform had a feature allowing users with the Instructor role to have custom unique subdomains like instructorname.example.com for their accounts.

This was an important finding because:

  • It explained why wildcard DNS routing was enabled by design.
  • It highlighted potential attack vectors to test later, such as trying to access or manipulate another instructor’s subdomain and private content.

Phase 2: Active Reconnaissance & Content Discovery

After wrapping up the passive reconnaissance phase, I shifted to active enumeration — sending traffic directly to the target infrastructure to map open services, identify administrative panels, and profile the underlying technology stack.

1. Subdomain Enumeration

Building on the passive findings, I performed additional subdomain enumeration using amass in active mode and ffuf with custom wordlists to discover any hidden hostnames. Since the target had wildcard DNS enabled, I had to carefully validate the results and distinguish real assets from false positives.

2. Port Scanning

Since the origin IP was not protected by a reverse proxy such as Cloudflare, direct port scanning was critical to uncover exposed services (such as FTP, SSH, MySQL, or ports like 3000 and 8000).

While most subdomains shared the same origin IP, I noticed that the staging subdomain resolved to a different IP address. Running an nmap scan revealed that SSH (port 22) was open.

I decided to investigate further by running standard nmap scripts to fingerprint the service:

nmap -p 22 -sV -sC staging.example.com

While this path did not lead to further access or unpatched CVEs, I still documented the security concerns and recommendations in the report, such as:

  • Enforcing key-based authentication over passwords.
  • Implementing brute-force protection mechanisms like Fail2ban.
  • Reviewing SSH exposure and access controls.

I also identified an administrative panel running CloudPanel on port 8443.

3. Application Mapping & Endpoint Fuzzing

After mapping the infrastructure, I moved my focus toward the applications themselves. I started by discovering common endpoints using custom wordlists, followed by crawling to understand the application’s structure and available functionality.

During this process, I discovered that the main web portal was built with Angular.

Tip: To confirm the Angular framework, you will typically see JavaScript bundles such as main.[hash].js and polyfills.[hash].js loaded. You can search across the loaded files in your browser's network tab for Angular-specific keywords such as isAngularZone, runOutsideAngular, or ng-container. This can also be done using automated technology detection tools.

One thing I always look for when encountering modern frontend technologies like React and Angular is the information exposed through client-side code. Frontend bundles can often reveal API endpoints, request parameters, application routes, and hidden functionality, which can significantly speed up the testing process. In some cases, exposed source maps can even reveal the original source code, reducing the time required for manual analysis and fuzzing.

I also identified several interesting application components, including the Instructor login panel and separate API endpoints related to administrative functionality.

Keeping all information and findings structured in my workspace early on saved a ton of time later, making it effortless to resume my sessions day after day and prioritize which assets to look at first.

Phase 3: Vulnerability Discovery

With the application’s structure mapped and the technologies identified, my goal shifted from enumeration to understanding how each feature actually behaved. Automated scanners are great for finding low-hanging fruit, but they rarely understand business logic or unexpected application behavior.

Keeping track of what to test is important during a long assessment. Using references such as the OWASP Web Application Penetration Testing Checklist helps ensure you cover all important areas during an assessment. I also maintain my own customized checklist to keep my testing process organized and avoid relying only on memory.

1. Server-Side Testing

I began by analyzing the application’s server-side behavior, paying close attention to HTTP responses, request handling, and how the backend reacted to unexpected input. I also looked for ways to identify underlying technologies and their versions, as outdated software can introduce known vulnerabilities worth investigating.

Some of the areas I reviewed included:

  • Changing HTTP methods to identify inconsistent request handling.
  • Modifying parameter names, values, and data types.
  • Testing for possible header injection issues.
  • Triggering error conditions to observe how the application handled failures.
  • Reviewing cryptographic implementations and security headers.

Tip: Even if the application doesn’t expose server information through response headers, don’t stop there. Error pages, redirects, protocol mismatches, malformed requests, changing the Content-Type header, and unexpected input can reveal valuable clues about the underlying technology, or even verbose debug information.

During testing, I identified the application’s backend stack and confirmed it was running Laravel. And one thing immediately came to my mind: testing for Mass Assignment.

One common mistake some Laravel developers make is passing unfiltered user-controlled request data directly into models without properly restricting which fields can be updated. For example:

Model::create($requestData);

If the model doesn’t properly restrict attributes using $fillable or $guarded, an attacker can modify fields that were never intended to be user-controlled.

My assumption was correct. After testing several endpoints, I was able to successfully find and confirm multiple locations vulnerable to Mass Assignment.

2. Client-Side Testing

I also reviewed how user-controlled data was handled after being stored. Even if a value was not immediately visible in the current interface, I checked where and how it was later displayed.

A field that appears harmless from a user perspective might be rendered somewhere else, such as an administrative dashboard, management portal, exports, or internal tools. These hidden paths can introduce vulnerabilities like Stored XSS, Blind XSS, or other injection issues.

Get Ahmed Atef’s stories in your inbox

Join Medium for free to get updates from this writer.

During this phase, I also reviewed:

  • JavaScript files for exposed API endpoints, sensitive information, or hidden functionality.
  • Client-side validation to verify whether security checks were enforced only in the browser and could be bypassed.
  • Sensitive data exposure through browser storage, cookies, local storage, or API responses.
  • DOM-based vulnerabilities caused by unsafe client-side processing.
  • Access control assumptions made by the frontend, such as hidden buttons or restricted features that could still be accessed directly through API calls.

3. Business Logic Testing

This is where I believe creativity and mindset make the biggest difference during a penetration test.

Instead of only testing expected workflows, I tried to think about different scenarios that developers may not have considered. I tested how the application behaved when assumptions were broken — changing IDs, modifying paths, manipulating workflows, attempting actions from different user perspectives, and bypassing multi-step workflows by directly submitting final requests.

By doing this, I discovered several IDOR vulnerabilities, information disclosure issues, and business logic flaws that could not have been identified through automated tools alone.

Phase 4: Exploitation — Achieving Remote Code Execution (RCE)

While exploring the Instructor panel and its available features, I wanted to see if I could push beyond the previous findings and achieve full system compromise. That is when one specific feature caught my attention for deeper testing: the category logo file upload when creating categories.

The application only accepted these specific formats: JPEG, PNG, and SVG.

At first, I treated it like any other standard file upload functionality, testing common scenarios including SVG-based XSS, malicious file payloads, extension bypasses, and other typical upload attacks.

The application successfully blocked my initial SVG injection attempts and common payload variations. After some attempts, I managed to bypass one filter, but the payload failed to execute due to strict Content Security Policy (CSP) headers.

I continued testing the image content itself. I tried embedding a payload into image metadata (EXIF comments) — a classic technique used to inject PHP code into a legitimate image’s metadata field:

exiftool -comment="<?php phpinfo(); ?>" logo.png

However, the injected comment did not execute on the server.

Analyzing the Upload Validation Logic

From the behavior I observed, the upload process appeared to rely on multiple checks:

  • Required a valid image Content-Type header.
  • Verified the file contents matched an accepted image format.
  • Blocked dangerous file extensions like .php or .phtml.
  • Restricted the maximum file size.

Additionally, the uploaded file was renamed based on a specific pattern. logo.png saved as {date}-logo-{random-chars}.png.

Shifting my focus toward filename handling, I tested different exploit techniques, including path traversal (../, ./), encoded extensions, filename manipulation, and special characters.

I also tested null bytes and newline characters such as %00 and %0a. For example, .php%00.png was converted into .php_00-b2ba3817.png. I noticed interesting behavior in how the application processed filenames: some special characters were removed, some were replaced with underscores (_), and some attempts were completely blocked.

I remembered back in 2022, I discovered an RCE vulnerability in an ASP.NET application via file upload, where I was able to bypass extension handling using a null byte directly in the raw request. After passing validation, the application saved the file to disk while dropping its original extension.

I tried a similar approach here, but it did not work.

After testing a few edge-case filenames containing symbols like ~!@#$%^&*, I figured out the pattern: special characters at the beginning and end of the filename were removed, while special characters in the middle were replaced with a single underscore.

So, ${logo}.png will still be: {date}-logo-{random-chars}.png. This made me think about applying the same behavior to the extension itself.

I tested .php.%00png, which was converted into .php-14c9b8b3.00png. The application was also removing special characters at the beginning of the extension!

So I came up with this idea. Instead of sending a blocked extension like .php, I added a special character right before the extension’s letters: .%php. This bypassed the extension block! but when I tried to upload a PHP code, it failed because the file was no longer recognized as a valid image content.

Suspecting the application was only checking the file’s magic bytes, I intercepted a legitimate image upload, kept the first part containing the initial PNG signature header (89 50 4E 47 0D 0A 1A 0A), and appended my code:

Press enter or click to view image in full size
Example of the crafted file upload request
Press enter or click to view image in full size
PNG signature

The file was successfully uploaded as: {date}-logo.png-{random}.php.

Because the uploaded file was ultimately stored with a .php extension in a web-accessible location handled by PHP, the interpreter processed its contents. The PNG binary data at the beginning of the file was treated as plain output until PHP reached the embedded <?php tag, where the injected code was executed.

Once the code successfully executed and confirmed an RCE vulnerability on a production system, I immediately notified the client through the agreed reporting channel in accordance with the Rules of Engagement.
I assisted in implementing a hotfix to secure the application. Following their approval to proceed, I conducted a limited post-exploitation assessment to evaluate the potential impact.

Phase 5: Post-Exploitation

First, I set out to answer three fundamental enumeration questions: Who am I? Where am I? And what can I do?

whoami
pwd
sudo -l

Running sudo -l revealed the following permission:

(ALL) NOPASSWD: /usr/bin/clpctlWrapper

Remember the CloudPanel installation I identified during reconnaissance?
While older versions of CloudPanel suffered from high-severity privilege escalation flaws via the clpctl utility, this target was running a fully patched version. Even so, the presence of clpctlWrapper in sudo -l meant I could still interact with certain administrative functionalities as the current user.

By checking the OS version and kernel release, I found that the server was running an outdated distribution with known vulnerabilities, including the “Copy Fail” local privilege escalation flaw (CVE-2026–31431).

Because actively testing such exploits carries the risk of corrupting memory or causing a full server outage (specifically by modifying the in-memory page cache of a targeted setuid binary like /usr/bin/su), running it on a production environment carried unnecessary risk. Therefore, I documented the findings and provided the appropriate remediation recommendations instead of attempting active exploitation.

Tip: Sometimes you have to be smart enough to know when to stop. Going one step further or running an automated script on a production server might prove a point, but it can also cause downtime or affect real users.

The Impact: Answering the Client’s Core Question

I could finally answer the client’s original question: “How far could an unprivileged attacker actually get?”

The reality went far beyond what should have been possible. Even without leveraging privilege escalation, I already had the ability to execute system commands, access plaintext credentials and secrets in the .env file, read the application code, access the database, and inspect log files.

Phase 6: Reporting & Remediation

After submitting a detailed report with comprehensive findings, notes, and references to the client, I analyzed the upload functionality to understand the exact attack chain and why the bypass was possible, despite the application already implementing filename sanitization measures such as removing null bytes and special characters.

Root Cause Analysis:

The upload functionality relied on multiple validation checks and custom file-handling logic.

1. The application first performed MIME type validation on the uploaded file, including allowed formats and maximum file size checks.

2. Once the original filename was retrieved using getClientOriginalName(), the application extracted the filename and extension parts:

$name = pathinfo($fileName, PATHINFO_FILENAME);
$extension = pathinfo($fileName, PATHINFO_EXTENSION);

3. The application then applied an extension blacklist to block known dangerous file types.

4. If the extension passed this check, the upload process continued with custom sanitization applied to both parts:

$name = sanitizeLogic($name);
$extension = sanitizeLogic($extension);

5. Finally, the sanitized values were reconstructed into:

$fileName = $timestamp.'-'.$name.'-'.$randomValue.'.'.$extension;

The root cause was not the file validation mechanism itself. While MIME type validation alone is not sufficient as a complete upload security control, the vulnerability resulted from the interaction between custom filename processing, extension filtering, and sanitization logic. Although the application attempted to make user-controlled filenames safe, the transformation process introduced unexpected edge cases that allowed the security controls to be bypassed.

Key Remediation Steps:

  • Replace extension blocklists with strict allowlist validation.
  • Verify the file type using both content validation and server-side checks.
  • Avoid using user-controlled filenames. Generate random server-side filenames based on the validated file type.
  • Remove unnecessary custom filename sanitization logic. Complex transformations can introduce unexpected bypasses.
  • Reprocess uploaded images before storage using a trusted image library to remove unexpected content and metadata leaks.
  • Review all file upload functionality across the application and apply consistent validation controls.
  • Ensure uploaded files are stored with appropriate permissions.
  • Harden PHP configuration by disabling dangerous functions in php.ini such as: disable_functions = exec,system,shell_exec,passthru,popen as an additional defense-in-depth measure.

Summary

For me, the real challenge of a pentest isn’t just about finding a single critical vulnerability and walking away. It’s about deeply understanding the infrastructure, uncovering as many meaningful security issues as possible, and delivering maximum value within the agreed Rules of Engagement and testing window.

Even in the age of AI, getting carried away and falling down rabbit holes hunting for overly complex exploits isn’t always the answer. Technology keeps evolving, yet severe security incidents still often stem from simple oversights and small misconfigurations. Use modern tools to improve your workflow, but never let them replace your own critical thinking.

Thanks for reading! I hope you found something useful that you can apply in your own security assessments.

Feel free to connect with me on LinkedIn.

--

--