UserInfo.exe


An unknown binary has been found in one of the SMB shares that is suspected to be used as a storage space to store common provisioning tools. While it is indeed [[Support_SMB#binary|written in C# and built on the .NET framework]], it’s compiled to the x86 architecture. It allows me to be able to execute the binary within Kali using mono to inspect the flow of execution.

┌──(kali㉿kali)-[~/…/htb/labs/support/smb]
└─$ mono UserInfo.exe
 
usage: UserInfo.exe [options] [commands]
 
options: 
  -v|--verbose        Verbose output                                    
 
commands: 
  find                Find a user                                       
  user                Get information about a user

Executing the binary alone shows the help menu While it has the -v flag for verbose output, it supports 2 commands; find and user

find


┌──(kali㉿kali)-[~/…/htb/labs/support/smb]
└─$ mono UserInfo.exe find --help
 
Usage: UserInfo.exe find [options] 
 
Options: 
  -first              First name                                        
  -last               Last name
 
┌──(kali㉿kali)-[~/…/htb/labs/support/smb]
└─$ mono UserInfo.exe user --help
 
Usage: UserInfo.exe user [options] 
 
Options: 
  -username           Username

Additional help menu is shown for each command with the --help flag

┌──(kali㉿kali)-[~/…/htb/labs/support/smb]
└─$ mono UserInfo.exe find       
[-] At least one of -first or -last is required.

As the name suggests, the find command appears to be used to find users, requiring at least one of the follow flags; -first or -last

user


┌──(kali㉿kali)-[~/…/htb/labs/support/smb]
└─$ mono UserInfo.exe user
unable to parse command 'user' reason: Required option '-username' not found!

The user command, on the other hand, requires the -username flag and seems to return information about given user

┌──(kali㉿kali)-[~/…/htb/labs/support/smb]
└─$ mono UserInfo.exe user -username ford.victoria
[-] exception: Connect Error
 
┌──(kali㉿kali)-[~/…/htb/labs/support/smb]
└─$ mono UserInfo.exe -v user -username ford.victoria
[*] Getting data for ford.victoria
[-] exception: Connect Error

Testing out the execution with one of the domain users found during RID Cycling attack returns a connection error

┌──(kali㉿kali)-[~/…/htb/labs/support/smb]
└─$ mono UserInfo.exe find -last ford
[-] exception: Connect Error
 
┌──(kali㉿kali)-[~/…/htb/labs/support/smb]
└─$ mono UserInfo.exe -v find -last ford
[*] ldap query to use: (sn=ford)
[-] exception: Connect Error

While it shows the same connection error with the find command, the -v flag for verbose output reveals a critical information about the execution. The find command appears to be fetching user information via LDAP query

This would mean that the binary must be authenticating to the LDAP server as it requires a successful bind to fetch user information That being said, it is highly probable that the binary may contain a valid credential for authentication

I will open up Wireshark to inspect the outbound packets

Packet Capture


┌──(kali㉿kali)-[~/…/htb/labs/support/smb]
└─$ mono UserInfo.exe -v find -last ford
[*] LDAP query to use: (sn=ford)
[-] Exception: Connect Error

Attempting another find command while Wireshark is running

2 DNS queries first got sent out by the binary Interestingly, the binary appears to be misconfigured as those DNS queries are made to the domain itself, support.htb, instead of the supposed name server, which is dc.support.htb

”Misconfiguring” /etc/hosts


Since DNS queries are sent to the misconfigured host at support.htb, I would need to “misconfigure” the /etc/hosts file on Kali as well

Executing the binary again shows a complete different result this time after the initial 3-way tcp handshake, the binary sends out a LDAP simple authentication to the target LDAP server on the port 389 LDAP server on the port 389 is NOT encrypted, and data is sent across in CLEARTEXT

CLEARTEXT Credential


Inspecting the packet containing the LDAP simple authentication reveals the CLEARTEXT domain credential; ldap:nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz

While it is likely a valid domain as the ldap account indeed exists, additional validation is never a bad idea

I will first undo the “misconfiguration” on my end

Validation


┌──(kali㉿kali)-[~/archive/htb/labs/support]
└─$ impacket-getTGT support.htb/ldap@dc.support.htb -dc-ip $IP     
Impacket v0.11.0 - Copyright 2023 Fortra
 
password: nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz
[*] Saving ticket in ldap@dc.support.htb.ccache

Additional validation complete for the ldap account TGT generated for OPSEC

Decompile


Although the vulnerable custom binary has been already assessed and a CREARTEXT credential has been retrieved, decompilation can still be done to better understand the binary

Porting out the binary archive to a Windows host

I will then open it up in dnSpy

The LdapQuery class is defined in the UserInfo.Servies namespace There is also the Protected class at the bottom

This is the LdapQuery() function that sends out the LDAP simple authentication. The credential is stored to the password variable, fetched from the Protected.getPassword() function

The Protected class can be seen above, which includes the public getPassword() function as well as the private variables, enc_password and key

The getPassword() function basically does the following;

  • `byte[] array = Convert.FromBase64String(Protected.enc_password);
    • decodes the base64-encoded password stored in the enc_password variable and stores the result in the array byte array.
  • for (int i = 0; i < array.Length; i++) {array2[i] = (array[i] ^ Protected.key[i % Protected.key.Length] ^ 223);}
    • iterates through each byte in the array
      • performs a bitwise XOR operation on each byte of the array with the corresponding byte from the key array (cycling through the key if necessary) and 223
  • return Encoding.Default.GetString(array2);
    • converts the modified byte array array2 back to a string using the default encoding and returns it as the result of the getPassword method

Now that I understand how the getPassword() function works, I could reverse engineer the process to decrypt the encrypted string (0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E) with the key array from the ASCII, armando

Reverse Engineering


using System;
 
internal class Program
{
    public static void Main(string[] args)
    {
        string decryptedPassword = Protected.decryptPassword();
        console.writeline("decrypted password: " + decryptedPassword);
    }
}
 
internal class Protected
{
    // This is the base64-encoded encrypted password
    private static string enc_password = "0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E";
 
    // This is a byte array representation of the decryption key "armando"
    private static byte[] key = System.Text.Encoding.ASCII.GetBytes("armando");
    
    // This method performs a one-way decryption and returns the decrypted string.
    public static string decryptPassword()
    {
        byte[] array = Convert.FromBase64String(Protected.enc_password);
        byte[] array2 = array;
        for (int i = 0; i < array.Length; i++)
        {
            array2[i] = (byte)(array[i] ^ Protected.key[i % Protected.key.Length] ^ 223);
        }
        return System.Text.Encoding.Default.GetString(array2);
    }
}

This is the reverse-engineered program that I wrote It does exactly the opposite of the getPassword() function

┌──(kali㉿kali)-[~/…/htb/labs/support/smb]
└─$ mcs decryption.cs -out:decryption.exe -debug

Cross-compiling with mcs

┌──(kali㉿kali)-[~/…/htb/labs/support/smb]
└─$ mono decryption.exe                                                               
decrypted password: nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz

There is the decrypted password; nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz It matches the one I saw earlier in CLEARTEXT