This is a Windows Server 2019 machine that hosts a web application used by a group of SOC analysts for network and incident monitoring purposes. Access to the web application is gained using ASREPRoast bruteforce and LDAP blind injection attacks. Once inside the analysts web dashboard, the report upload feature is abused to transfer a PHP reverse shell in the server, since no upload filters are in place. For the system escalation part, a DLL hijacking vulnerability affecting the Snort IT monitoring tool is exploited.
Looks like a Windows domain controller. Enumerate the open ports.
> nmap $target -p53,80,88,135,139,445,464,593,636,3268,3269,3306,5985,9389,33060,47001 -sV -sC -Pn -vvStartingNmap7.93 ( https://nmap.org ) at 2024-01-20 17:42 ESTHostisup,receiveduser-set (0.045s latency).Scannedat2024-01-2017:42:18ESTfor54sPORTSTATESERVICEREASONVERSION53/tcpopendomainsyn-ackSimpleDNSPlus80/tcpopenhttpsyn-ackMicrosoftHTTPAPIhttpd2.0 (SSDP/UPnP)|_http-server-header:Microsoft-HTTPAPI/2.0|_http-title:NotFound88/tcpopenkerberos-secsyn-ackMicrosoftWindowsKerberos (server time:2024-01-2022:43:13Z)135/tcpopenmsrpcsyn-ackMicrosoftWindowsRPC139/tcpopennetbios-ssnsyn-ackMicrosoftWindowsnetbios-ssn445/tcpopenmicrosoft-ds?syn-ack464/tcpopenkpasswd5?syn-ack593/tcpopenncacn_httpsyn-ackMicrosoftWindowsRPCoverHTTP1.0636/tcpopentcpwrappedsyn-ack3268/tcpopenldapsyn-ackMicrosoftWindowsActiveDirectoryLDAP (Domain: analysis.htb0.,Site:Default-First-Site-Name)3269/tcpopentcpwrappedsyn-ack3306/tcpopenmysqlsyn-ackMySQL (unauthorized)5985/tcpopenhttpsyn-ackMicrosoftHTTPAPIhttpd2.0 (SSDP/UPnP)|_http-server-header:Microsoft-HTTPAPI/2.0|_http-title:NotFound9389/tcpopenmc-nmfsyn-ack.NETMessageFraming33060/tcpopenmysqlx?syn-ack|fingerprint-strings:|DNSStatusRequestTCP,LDAPSearchReq,NotesRPC,SSLSessionReq,TLSSessionReq,X11Probe,afp:|Invalidmessage"| HY000| LDAPBindReq:| *Parse error unserializing protobuf message"|HY000|oracle-tns:|Invalidmessage-frame."|_ HY00047001/tcp open http syn-ack Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)|_http-server-header: Microsoft-HTTPAPI/2.0|_http-title: Not Found1 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.93%I=7%D=1/20%Time=65AC4C55%P=x86_64-pc-linux-gnu%r(GSF:enericLines,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(HTTPOptions,9,"\x05\0\0SF:\0\x0b\x08\x05\x1a\0")%r(RTSPRequest,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%SF:r(RPCCheck,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(DNSVersionBindReqTCP,9,"SF:\x05\0\0\0\x0b\x08\x05\x1a\0")%r(DNSStatusRequestTCP,2B,"\x05\0\0\0\x0bSF:\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20messageSF:\"\x05HY000")%r(Help,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(SSLSessionReq,SF:2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fSF:Invalid\x20message\"\x05HY000")%r(TerminalServerCookie,9,"\x05\0\0\0\x0SF:b\x08\x05\x1a\0")%r(TLSSessionReq,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(KerSF:beros,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(SMBProgNeg,9,"\x05\0\0\0\x0b\SF:x08\x05\x1a\0")%r(X11Probe,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\xSF:01\x08\x01\x10\x88'\x1a\x0fInvalid\x20message\"\x05HY000")%r(FourOhFourSF:Request,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(LPDString,9,"\x05\0\0\0\x0bSF:\x08\x05\x1a\0")%r(LDAPSearchReq,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0SF:\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20message\"\x05HY000")%r(LDAPSF:BindReq,46,"\x05\0\0\0\x0b\x08\x05\x1a\x009\0\0\0\x01\x08\x01\x10\x88'\SF:x1a\*Parse\x20error\x20unserializing\x20protobuf\x20message\"\x05HY000"SF:)%r(SIPOptions,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(TerminalServer,9,"\xSF:05\0\0\0\x0b\x08\x05\x1a\0")%r(NCP,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(SF:NotesRPC,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88SF:'\x1a\x0fInvalid\x20message\"\x05HY000")%r(JavaRMI,9,"\x05\0\0\0\x0b\x0SF:8\x05\x1a\0")%r(WMSRequest,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(oracle-tSF:ns,32,"\x05\0\0\0\x0b\x08\x05\x1a\0%\0\0\0\x01\x08\x01\x10\x88'\x1a\x16SF:Invalid\x20message-frame\.\"\x05HY000")%r(ms-sql-s,9,"\x05\0\0\0\x0b\x0SF:8\x05\x1a\0")%r(afp,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\SF:x01\x10\x88'\x1a\x0fInvalid\x20message\"\x05HY000")%r(giop,9,"\x05\0\0\SF:0\x0b\x08\x05\x1a\0");ServiceInfo:Host:DC-ANALYSIS; OS:Windows; CPE:cpe:/o:microsoft:windowsHostscriptresults:|smb2-security-mode:|311:|_Messagesigningenabledandrequired|p2p-conficker:|CheckingforConficker.Corhigher...|Check1 (port 64736/tcp): CLEAN (Couldn't connect)| Check 2 (port 7365/tcp): CLEAN (Couldn'tconnect)|Check3 (port 44409/udp): CLEAN (Failedtoreceivedata)|Check4 (port 9653/udp): CLEAN (Timeout)|_0/4checksarepositive:HostisCLEANorportsareblocked|_clock-skew:49s|smb2-time:|date:2024-01-20T22:43:49|_start_date:N/ANmapdone:1IPaddress (1 hostup) scanned in 54.67 seconds
DNS enumeration.
> nslookup> server 10.10.11.250Defaultserver:10.10.11.250Address:10.10.11.250#53> analysis.htbServer:10.10.11.250Address:10.10.11.250#53Name:analysis.htbAddress:10.10.11.250
LDAP enumeration.
> nmap $target -p389,636 -script discoveryStartingNmap7.93 ( https://nmap.org ) at 2024-01-24 05:56 ESTPre-scanscriptresults:|_hostmap-robtex:*TEMPORARILYDISABLED*duetochangesinRobtex's API. See https://www.robtex.com/api/| targets-asn:|_ targets-asn.asn is a mandatory parameter|_http-robtex-shared-ns: *TEMPORARILY DISABLED* due to changes in Robtex'sAPI.Seehttps://www.robtex.com/api/Nmapscanreportforanalysis.htb (10.10.11.250)Hostisup (0.089s latency).PORTSTATESERVICE389/tcpopenldap|ldap-rootdse:|LDAPResults|<ROOT>|domainFunctionality:7|forestFunctionality:7|domainControllerFunctionality:7|rootDomainNamingContext:DC=analysis,DC=htb|ldapServiceName:analysis.htb:dc-analysis$@ANALYSIS.HTB|isGlobalCatalogReady:TRUE|supportedSASLMechanisms:GSSAPI|supportedSASLMechanisms:GSS-SPNEGO|supportedSASLMechanisms:EXTERNAL|supportedSASLMechanisms:DIGEST-MD5|supportedLDAPVersion:3|supportedLDAPVersion:2|supportedLDAPPolicies:MaxPoolThreads|supportedLDAPPolicies:MaxPercentDirSyncRequests|supportedLDAPPolicies:MaxDatagramRecv|supportedLDAPPolicies:MaxReceiveBuffer|supportedLDAPPolicies:InitRecvTimeout|supportedLDAPPolicies:MaxConnections|supportedLDAPPolicies:MaxConnIdleTime|supportedLDAPPolicies:MaxPageSize|supportedLDAPPolicies:MaxBatchReturnMessages|supportedLDAPPolicies:MaxQueryDuration|supportedLDAPPolicies:MaxDirSyncDuration|supportedLDAPPolicies:MaxTempTableSize|supportedLDAPPolicies:MaxResultSetSize|supportedLDAPPolicies:MinResultSets|supportedLDAPPolicies:MaxResultSetsPerConn|supportedLDAPPolicies:MaxNotificationPerConn|supportedLDAPPolicies:MaxValRange|supportedLDAPPolicies:MaxValRangeTransitive|supportedLDAPPolicies:ThreadMemoryLimit|supportedLDAPPolicies:SystemMemoryLimitPercent|supportedControl:1.2.840.113556.1.4.319|supportedControl:1.2.840.113556.1.4.801|supportedControl:1.2.840.113556.1.4.473|supportedControl:1.2.840.113556.1.4.528|supportedControl:1.2.840.113556.1.4.417|supportedControl:1.2.840.113556.1.4.619|supportedControl:1.2.840.113556.1.4.841|supportedControl:1.2.840.113556.1.4.529|supportedControl:1.2.840.113556.1.4.805|supportedControl:1.2.840.113556.1.4.521|supportedControl:1.2.840.113556.1.4.970|supportedControl:1.2.840.113556.1.4.1338|supportedControl:1.2.840.113556.1.4.474|supportedControl:1.2.840.113556.1.4.1339|supportedControl:1.2.840.113556.1.4.1340|supportedControl:1.2.840.113556.1.4.1413|supportedControl:2.16.840.1.113730.3.4.9|supportedControl:2.16.840.1.113730.3.4.10|supportedControl:1.2.840.113556.1.4.1504|supportedControl:1.2.840.113556.1.4.1852|supportedControl:1.2.840.113556.1.4.802|supportedControl:1.2.840.113556.1.4.1907|supportedControl:1.2.840.113556.1.4.1948|supportedControl:1.2.840.113556.1.4.1974|supportedControl:1.2.840.113556.1.4.1341|supportedControl:1.2.840.113556.1.4.2026|supportedControl:1.2.840.113556.1.4.2064|supportedControl:1.2.840.113556.1.4.2065|supportedControl:1.2.840.113556.1.4.2066|supportedControl:1.2.840.113556.1.4.2090|supportedControl:1.2.840.113556.1.4.2205|supportedControl:1.2.840.113556.1.4.2204|supportedControl:1.2.840.113556.1.4.2206|supportedControl:1.2.840.113556.1.4.2211|supportedControl:1.2.840.113556.1.4.2239|supportedControl:1.2.840.113556.1.4.2255|supportedControl:1.2.840.113556.1.4.2256|supportedControl:1.2.840.113556.1.4.2309|supportedControl:1.2.840.113556.1.4.2330|supportedControl:1.2.840.113556.1.4.2354|supportedCapabilities:1.2.840.113556.1.4.800|supportedCapabilities:1.2.840.113556.1.4.1670|supportedCapabilities:1.2.840.113556.1.4.1791|supportedCapabilities:1.2.840.113556.1.4.1935|supportedCapabilities:1.2.840.113556.1.4.2080|supportedCapabilities:1.2.840.113556.1.4.2237|subschemaSubentry:CN=Aggregate,CN=Schema,CN=Configuration,DC=analysis,DC=htb|serverName:CN=DC-ANALYSIS,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=analysis,DC=htb|schemaNamingContext:CN=Schema,CN=Configuration,DC=analysis,DC=htb|namingContexts:DC=analysis,DC=htb|namingContexts:CN=Configuration,DC=analysis,DC=htb|namingContexts:CN=Schema,CN=Configuration,DC=analysis,DC=htb|namingContexts:DC=DomainDnsZones,DC=analysis,DC=htb|namingContexts:DC=ForestDnsZones,DC=analysis,DC=htb|isSynchronized:TRUE|highestCommittedUSN:373247|dsServiceName:CN=NTDSSettings,CN=DC-ANALYSIS,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=analysis,DC=htb|dnsHostName:DC-ANALYSIS.analysis.htb|defaultNamingContext:DC=analysis,DC=htb|currentTime:20240124105741.0Z|_configurationNamingContext:CN=Configuration,DC=analysis,DC=htb636/tcpopenldapsslServiceInfo:Host:DC-ANALYSIS; OS:WindowsHostscriptresults:|_fcrdns:FAIL (No PTRrecord)|dns-brute:|_DNSBrute-forcehostnames:Noresults.Nmapdone:1IPaddress (1 hostup) scanned in 16.95 seconds
If we browse this site with Firefox we receive an error: "Missing parameter".
Let's find out which is that missing parameter the application is expecting. Capture the request with Burpsuite, right-click and save to file request.txt, then edit the text file marking the fields to fuzz with ffuf
GET /users/list.php?FUZZ HTTP/1.1Host:internal.analysis.htbUser-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflate, brConnection:closeUpgrade-Insecure-Requests:1
Launch ffuf with the text file as argument to find the parameter.
So it seems the application is expecting parameter called name. Check it with Firefox.
It seems the application is taking user input in the parameter name and using it to launch a query against a database to fetch user data. Then the retrieved data is presented in the web page.
At this point we can consider enumeration finished as we have found everything we need to begin exploitation. Wrapping up, this is what we have found:
The host is a domain controller, and domain name is analysis.htb
In the internal subdomain, there is another site at http://internal.analysis.htb/users/list.php where users can query a database by means of a parameter called name. According to the list of services running, this database should be MySQL or LDAP.
USER
The employees login portal could be an entry point, but we need at least a list of valid usernames. Two options that come to mind to bruteforce usernames are an ASREPRoast attack or an RID bruteforce. Since we do not have SMB credentials, we will opt for ASREPRoast.
Two tools we can use to bruteforce usernames are GetNPUsers.py or Kerbrute. In summary, both of send a TGT request with no pre-authentication and check the KDC reply. If the user exists, the KDC will prompt pre-authentication; if not, it will reply with an user not found message. Therefore, we can find out which usernames exist and which ones not.
With GetNPUsers.py, launch an ASREPRoast attack and grep the results.
It is able to dump the usernames but takes a lot of time. There is a faster tool for this purpose called Kerbrute which essentially does the same: initiate a no-preauth TGT request and checks what the KDC replies.
In any case, both methods return the same list of usernames. Testing them in the web site we discover the only username which is returning data in the name parameter is technician
Now let's analyze what could be happening in the backend. It seems it is taking user input in the parameter name and using it to query a database which must be either MySQL or LDAP. Therefore, this parameter could be vulnerable to injections.
Let's verify first if the parameter is injectable in MySQL with sqlmap
The tool reports the parameter does not seem to be injectable in MySQL.
[08:44:26] [WARNING] GET parameter 'name' does not seem to be injectable[08:44:26] [CRITICAL] all tested parameters donotappeartobeinjectable.
The other option to try is LDAP injection. For this we need to find out which LDAP attributes are present in the database for user technician. This can be done fuzzing LDAP with Burpsuite.
To fuzz the application, choose a payload and capture the request with Burpsuite, then send the request to intruder. In this case, the payload used is this one:
Finally, according to our enumeration findings, we know the LDAP query is successful when the technician data is showed in the web site. Therefore, we will know if an LDAP attribute exists when the word "technician" is in the HTTP response. Configure the Burpsuite grep match to mark HTTP responses containing this word.
Run the attack and verify the responses containing the word "technician" in the results.
We notice all the attributes found are common in Active Directory except for the attribute description. This is optional and sometimes is used by system admins to add additional information such as passwords.
To dump the contents of the technician attribute description we will use a blind LDAP injection attack (https://book.hacktricks.xyz/pentesting-web/ldap-injection#blind-ldap-injection). Iterating over ASCII characters, and knowing the OK/NOK criteria (in this case, whether the response contains the text "technician" or not), we can dump the field contents. The method is similar to the process of a blind SQL injection.
The proposed blind LDAP injection script in Python to bruteforce the technician description field is the following:
Since we are using wildcards in the pattern URL, we have to take into account the case an asterisk (*) is used in the middle of the password.
Because of the same reason, there will always be an asterisk at the end of the password found (a trailing character). Bear in mind trailing characters must be removed at the end of the password before finishing the process.
The description field looks like a password. Let's try credential technician@analysis.htb:97NTtl*4QP96Bv in the employees portal to login into the SOC dashboard.
Once inside the web application, we can upload a PHP reverse shell by abusing the SOC report feature, since there is no upload filter in place. The shell used is this one:
Which can be found in Kali location /usr/share/webshells/php/simple-backdoor.php. Just upload the file and inspect the traffic with Burpsuite. The HTTP response contains the upload path.
In fact, the upload path is /dashboard/uploads/simple.php
Use this PHP shell to send a reverse shell to our Kali machine with powercat. Serve powercat.ps1 with a Python HTTP server, then download and execute from memory using Powershell's downloadstrings. The payload to use is:
Remember to URL encode to avoid problematic characters in the URL.
A reverse shell is received on port 1919. Although it is not valid to dump user flag, we can enumerate the system from it.
> systeminfoNom de l'hôte: DC-ANALYSISNom du système d'exploitation: Microsoft Windows Server 2019 StandardVersion du système: 10.0.17763 N/A version 17763Type du système: x64-based PC
After enumeration, we find out the password of user jdoe is configured in the winlogon registry key. We retrieve it by means of a registry query.
According to them, to exploit the vulnerability we are supposed to place in a remote shared folder a malicious tcapi.dll along with a .pcap capture file in the same folder. Then run Snort and let the application load the malicious DLL. Unfortunately, this procedure was tested and did not work.
Enumerating further in the Snort folder, we find a configuration file at C:\Snort\bin\snort.conf containing information about pre-processor libraries.
In summary, it seems Snort load first dynamic DLL's located at the pre-processor path c:\Snort\lib\snort_dynamicpreprocessor
Create an msfvenom payload for a malicious DLL called sf_engine.dll and copy to c:\snort\lib\snort_dynamicpreprocessor