Running a full port nmap stealth scan reveals that this is built like a DC:
PORTSTATESERVICE53/tcpopendomain88/tcpopenkerberos-sec135/tcpopenmsrpc139/tcpopennetbios-ssn389/tcpopenldap445/tcpopenmicrosoft-ds464/tcpopenkpasswd5593/tcpopenhttp-rpc-epmap636/tcpopenldapssl3268/tcpopenglobalcatLDAP3269/tcpopenglobalcatLDAPssl5985/tcpopenwsman9389/tcpopenadws49664/tcpopenunknown49667/tcpopenunknown49674/tcpopenunknown49686/tcpopenunknown49691/tcpopenunknown49709/tcpopenunknownNmapdone:1IPaddress (1 hostup) scanned in 113.94 seconds
Enumeration
SMB:
Further enumeration targets are LDAP and SMB.
Starting of with SMB, by running NMAP Scripts over it:
It turns out that we could enumerate a share via smb:
I ran an mget * and started running strings over the binaries, but the most interesting one is the UserInfo binary, that's for sure!
Reverse Engineering .NET Binary:
UserInfo.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections
This is a .NET binary, so I got ILSpy on my KaliBox and started reversing:
Load up the binary and start scavenging.
This is what we are looking for:
Reverse Engineering the Encryption Function:
We have function:
We have string variable enc_password which is base64.
We have byte array variable key which is taking ASCII string armando and turns it into a string of bytes.
We have function getPassword() which:
Has a byte array variable array that converts the enc_password from base64 to a string of bytes.
Has a byte array variable called array2 that is set to the length of array and it's identical. This will serve for storing the decrypted bytes.
Starts a for loop from 0 to array.length:
In this for loop each object of the array2 is:
(byte)(uint) Type Conversions: first it goes to uint then back to bytes.
key[i % key.Length] This takes the key variable which holds 'armando' and loops through it using modulo operator with i.
array[i] ^ key[i % key.Length] This is the first XOR operation, it compares the bytes of the array and the key.
^ 0xDF This is the second XOR operation, it adds another layer of security.
After processing all bytes, the function converts the entire decrypted byte array back to a string using the default encoding and returns it.
Explaining XOR Operation, Logic Gate:
A practical example of why this works:
XOR Logic cancels itself out or self-reverts:
This is a fairly simple formula to encrypt a password, that utilizes the basic XOR gate.
XOR is just a boolean logic gate: ((a and Not(b)) or (Not(a) and b)). Boolean Algebra, introduced by George Boole in the 19th Century.
We can verify this works by testing all possible combinations:
When a=0, b=0: ((0 and Not(0)) or (Not(0) and 0)) = ((0 and 1) or (1 and 0)) = (0 or 0) = 0
When a=0, b=1: ((0 and Not(1)) or (Not(0) and 1)) = ((0 and 0) or (1 and 1)) = (0 or 1) = 1
When a=1, b=0: ((1 and Not(0)) or (Not(1) and 0)) = ((1 and 1) or (0 and 0)) = (1 or 0) = 1
When a=1, b=1: ((1 and Not(1)) or (Not(1) and 1)) = ((1 and 0) or (0 and 1)) = (0 or 0) = 0
So, to recap:
We have base64 string;
We have bytes array of key;
We convert base64 to a string of bytes and store in array;
We define function to XOR each array byte with each key byte;
We XOR the result of the previous operation with the constant value 0xDF;
Convert the byte array back to base64 to get the encrypted string.
To decrypt:
Convert the result string back to byte array;
For each byte in the encrypted array:
XOR it with the corresponding byte from the key (cycling through the key)
XOR the result with 0xDF
Convert the final byte array to a string to get the original password
Remember XORing is an associative operation, meaning that the order of operations does not matter!
Let's say we've got the original byte P, the key byte K and the constant 0xDF, C;
Now, due to XOR's properties:
K ^ K cancels out to 0
Any value XORed with 0 remains unchanged
C ^ C cancels out to 0
Finding the Username:
Now we've got our password, but what's the username? Of course, I tried armando ! Doesn't work :D
So... I checked the LdapQuery function, we've got our answer:
There is a DirectoryEntry constructor that states the protocol://adress, username, password.
Bloodhound:
Now I ran bloodhound:
LDAP:
I think this box is super related to LDAP now, decided to run few ldap queries:
Foothold
We just got ourselves a plain-text password :) for the user support. Now, remember WinRM is on. Let's use that to connect.evil-winrm -i 10.10.11.174 -u support -p 'Ironside47pleasure40Watchful'
PrivEsc - RBCD
This a RBCD Attack, for more info please check this article.
Requirements:
Once we are on the box, I loaded up SharpHound. We have GenericAll over the DC through our group and we also have SeMachineAccountPrivilege Add workstations to domain Enabled
Launching the attack:
We will now abuse our SeMachineAccountPrivilege to add a new computer in the domain. We will use impacket-addcomputer for it:
Now, let's use our GenericAll rights to write an attribute to the DC to include a DACL where we can delegate from our ATTACKCOMPUTER$ to the DC:
Now let's go on the target host where we transferred Rubeus and check our hash:
01111001 (encrypted result)
⊕ 11001100 (same key as before)
---------
10110101 (original value restored!)
/**
* Exclusive-or gate:
* if ((a and Not(b)) or (Not(a) and b)) out = 1, else out = 0
*/
CHIP Xor {
IN a, b;
OUT out;
PARTS:
Not(in=a, out=notA);
Not(in=b, out=notB);
And(a=notA, b=b, out=notAandB);
And(a=a, b=notB, out=AandnotB);
Or(a=notAandB, b=AandnotB, out=out);
}
Encrypted = (P ^ K) ^ C
Decrypted = (Encrypted ^ K) ^ C
= (((P ^ K) ^ C) ^ K) ^ C
C:\Users\support\Downloads>whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ============================== =======
SeMachineAccountPrivilege Add workstations to domain Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled