UserExplorer.exe


The custom program, UserExplorer.exe, must be transferred to a Windows host for decompilation

┌──(kali㉿kali)-[~/…/htb/labs/infiltrator/Output_Messenger]
└─$ scp ./UserExplorer.exe admin@10.1.1.19:/Users/admin/Desktop/                                                                  
admin@10.1.1.19's password: 
UserExplorer.exe                                                                                      100% 6144     9.2MB/s   00:00    

Transfer complete

Static Analysis


Opening it up in dnSpy, the binary has 2 classes; Decryptor and LdapApp

Decryptor Class


using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
 
// Token: 0x02000002 RID: 2
public class Decryptor
{
	// Token: 0x06000002 RID: 2 RVA: 0x00002058 File Offset: 0x00000258
	public static string DecryptString(string key, string cipherText)
	{
		string result;
		using (Aes aes = Aes.Create())
		{
			aes.Key = Encoding.UTF8.GetBytes(key);
			aes.IV = new byte[16];
			ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);
			using (MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(cipherText)))
			{
				using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read))
				{
					using (StreamReader streamReader = new StreamReader(cryptoStream))
					{
						result = streamReader.ReadToEnd();
					}
				}
			}
		}
		return result;
	}
}

The Decryptor class has the DecryptString(key, cipherText) function, and it’s responsible for decrypting a cipher text with a key

LdapApp Class


using System;
using System.DirectoryServices;
 
// Token: 0x02000003 RID: 3
internal class LdapApp
{
	// Token: 0x06000004 RID: 4 RVA: 0x0000213C File Offset: 0x0000033C
	private static void Main(string[] args)
	{
		string path = "LDAP://dc01.infiltrator.htb";
		string text = "";
		string text2 = "";
		string text3 = "";
		string text4 = "winrm_svc";
		string cipherText = "TGlu22oo8GIHRkJBBpZ1nQ/x6l36MVj3Ukv4Hw86qGE=";
		int i = 0;
		while (i < args.Length)
		{
			string text5 = args[i].ToLower();
			if (text5 != null)
			{
				if (!(text5 == "-u"))
				{
					if (!(text5 == "-p"))
					{
						if (!(text5 == "-s"))
						{
							if (!(text5 == "-default"))
							{
								goto IL_C2;
							}
							text = text4;
							text2 = Decryptor.DecryptString("b14ca5898a4e4133bbce2ea2315a1916", cipherText);
						}
						else
						{
							text3 = args[i + 1];
						}
					}
					else
					{
						text2 = args[i + 1];
					}
				}
				else
				{
					text = args[i + 1];
				}
				i += 2;
				continue;
			}
			IL_C2:
			Console.WriteLine(string.Format("Invalid argument: {0}", args[i]));
			return;
		}
		if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2) || string.IsNullOrEmpty(text3))
		{
			Console.WriteLine("Usage: UserExplorer.exe -u <username> -p <password>  -s <searchedUsername> [-default]");
			Console.WriteLine("To use the default credentials: UserExplorer.exe -default -s userToSearch");
			return;
		}
		try
		{
			Console.WriteLine("Attempting Service Connection...");
			using (DirectoryEntry directoryEntry = new DirectoryEntry(path, text, text2))
			{
				Console.WriteLine("Service Connection Successful.");
				using (DirectorySearcher directorySearcher = new DirectorySearcher(directoryEntry))
				{
					directorySearcher.Filter = string.Format("(SAMAccountName={0})", text3);
					Console.WriteLine(string.Format("Search for {0} user...", text3));
					SearchResult searchResult = directorySearcher.FindOne();
					if (searchResult != null)
					{
						Console.WriteLine("User found. Details:");
						DirectoryEntry directoryEntry2 = searchResult.GetDirectoryEntry();
						Console.WriteLine(string.Format("Name: {0}", directoryEntry2.Properties["cn"].Value));
						Console.WriteLine(string.Format("EmailID: {0}", directoryEntry2.Properties["mail"].Value));
						Console.WriteLine(string.Format("Telephone Extension: {0}", directoryEntry2.Properties["telephoneNumber"].Value));
						Console.WriteLine(string.Format("Department: {0}", directoryEntry2.Properties["department"].Value));
						Console.WriteLine(string.Format("Job Title: {0}", directoryEntry2.Properties["title"].Value));
					}
					else
					{
						Console.WriteLine("User not found.");
					}
				}
			}
		}
		catch (Exception ex)
		{
			Console.WriteLine(string.Format("An error occurred: {0}", ex.Message));
		}
	}
}

It uses LDAP query to search and return requested user’s data

Hard-coded Credential


The -default flag appears to use the hard-coded credential of the winrm_svc account It also contains the key and cipherText variables

Reverse Engineering


using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
 
// Token: 0x02000002 RID: 2
public class Decryptor
{
	// Token: 0x06000002 RID: 2 RVA: 0x00002058 File Offset: 0x00000258
	public static string DecryptString(string key, string cipherText)
	{
		string result;
		using (Aes aes = Aes.Create())
		{
			aes.Key = Encoding.UTF8.GetBytes(key);
			aes.IV = new byte[16];
			ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);
			using (MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(cipherText)))
			{
				using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read))
				{
					using (StreamReader streamReader = new StreamReader(cryptoStream))
					{
						result = streamReader.ReadToEnd();
					}
				}
			}
		}
		return result;
	}
}
 
 
public class Program
{
	public static void Main(string[] args)
	{
		// Example key and cipherText
		string key = "your-16-char-key!"; // Ensure this is 16 bytes (128 bits), 24 bytes (192 bits), or 32 bytes (256 bits)
		string cipherText = "your-cipher-text-here"; // Base64-encoded cipher text
 
		if (args.Length == 2)
		{
			key = args[0];
			cipherText = args[1];
		}
		else
		{
			Console.WriteLine("Usage: Decryptor <key> <cipherText>");
			return;
		}
 
		try
		{
			string decrypted = Decryptor.DecryptString(key, cipherText);
			Console.WriteLine("Decrypted text: " + decrypted);
		}
		catch (Exception ex)
		{
			Console.WriteLine("Error: " + ex.Message);
		}
	}
}
 

From the original Decryptor class, I appended an entry point, Program, to run the Decryptor.DecryptString function with userinput

Compile


┌──(kali㉿kali)-[~/…/htb/labs/infiltrator/Output_Messenger]
└─$ mcs Decryptor.cs

Compiling

Execution


┌──(kali㉿kali)-[~/…/htb/labs/infiltrator/Output_Messenger]
└─$ ./Decryptor.exe b14ca5898a4e4133bbce2ea2315a1916 "TGlu22oo8GIHRkJBBpZ1nQ/x6l36MVj3Ukv4Hw86qGE="
Decrypted text: SKqwQk81tgq+C3V7pzc1SA==

The decrypted text still appears to be encrypted. It’s possible that it might have been double-encrypted.

┌──(kali㉿kali)-[~/…/htb/labs/infiltrator/Output_Messenger]
└─$ ./Decryptor.exe b14ca5898a4e4133bbce2ea2315a1916 "SKqwQk81tgq+C3V7pzc1SA=="
Decrypted text: WinRm@$svc^!^P

It was indeed double-encrypted The output appears to be a password; WinRm@$svc^!^P

Validation


┌──(kali㉿kali)-[~/archive/htb/labs/infiltrator]
└─$ impacket-getTGT INFILTRATOR.HTB/winrm_svc@dc01.infiltrator.htb -dc-ip $IP
Impacket v0.12.0.dev1 - Copyright 2023 Fortra
 
Password: WinRm@$svc^!^P
[*] Saving ticket in winrm_svc@dc01.infiltrator.htb.ccache

Successfully validated TGT generated for the winrm_svc account