CTF: M356 Email Signatures

I recently took part in the 2nd Cyberdrain CTF. This was my submission for the signature question

For this script I took this as a base https://github.com/12Knocksinna/Office365itpros/blob/master/UpdateOutlookSignature.PS1

I fixed all the errors in it, tweaked it a lot for the CTF and how I wanted it to work and also implemented a paired Azure Function that performs the actual lookup so local devices don’t need to have access to M365 Keys. The lookup script is then protected with a separate key. I have scripted it so that it would be run in the logged in user context by an RMM with the key, company logo URL and company name passed in.

Azure Function

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Write to the Azure Functions log stream.
Write-Host "M365 User Check Triggered"

######### Secrets #########
$ApplicationId = $ENV:ApplicationID
$ApplicationSecret = $ENV:ApplicationSecret
$RefreshToken = $ENV:Refreshtoken
$UserLookupKey = $ENV:UserLookupKey

import-module PartnerCenterLW
import-module MSOnlineLW
   
# Process Post Data
$UserData = $Request.RawBody | ConvertFrom-Json


if ($UserData.UserLookupKey -eq $UserLookupKey) {
    $ReturnResultCode = 200

    write-host "Creating credentials and tokens." -ForegroundColor Green
    $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, ($ApplicationSecret | Convertto-SecureString -AsPlainText -Force))
    $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal
    $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal
    write-host "Connecting to MSOL" -ForegroundColor Green

    Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken

    $UserTenantName = $UserData.UPN -split '@' | Select-Object -last 1
    $UserTenantGUID = (Invoke-WebRequest "https://login.windows.net/$UserTenantName/.well-known/openid-configuration" | ConvertFrom-Json).token_endpoint.Split('/')[3]

    $User = Get-MsolUser -TenantID $UserTenantGUID -UserPrincipalName $UserData.UPN

    if ($user) {
        $ReturnResultMessage = @{
            DisplayName     = $User.DisplayName
            StreetAddress   = $User.StreetAddress
            City            = $User.City
            PostalCode      = $User.PostalCode
            FirstName       = $User.FirstName
            LastName        = $User.LastName
            Title           = $User.Title
            TelephoneNumber = $User.PhoneNumber
            Mobile          = $User.MobilePhone
        }
    } else {
        $ReturnResultMessage = $null
    }

} else {
    $ReturnResultCode = 403
    $ReturnResultMessage = "No"

}


# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
        StatusCode = $ReturnResultCode
        Body       = $ReturnResultMessage
    })

Endpoint Script

# https://github.com/12Knocksinna/Office365itpros/blob/master/UpdateOutlookSignature.PS1

$env:UserLookupKey = "LookupKey"
$CompanyLogo = "https://yourlogo.com/logo.png"
$CompanyName = "Lukes Mega Company"

$SignatureName = 'Standard Signature' 

# This function can be adapted for your own lookup requirements.
function Get-UserDetails {
    param(
        [string]$UPN
    )

    $body = @{
        UPN           = $UPN
        UserLookupKey = $env:UserLookupKey
    } | ConvertTo-Json

    $Result = Invoke-WebRequest -uri "https://YOURAzureFunction.azurewebsites.net/api/QueryUser" -method POST -Body $Body
    Return $Result.Content | ConvertFrom-JSON

}

$LocalSignaturePath = (Get-Item env:appdata).Value + '\Microsoft\Signatures'
$HtmlPath = $LocalSignaturePath + '\' + $SignatureName + '.htm'

# Find the User Principal Name for the account as stored in the system registry
$UserAccount = Get-ItemProperty -Path HKCU:\Software\Microsoft\Office\Outlook\Settings -Name Accounts | Select-Object -ExpandProperty Accounts

$UserId = (ConvertFrom-Json $UserAccount).UserUpn[0]

if ($UserID) {

    # Retrieve the properties of the user from Azure Active Directory
    $UserProperties = Get-UserDetails -UPN $UserId

    # Find Outlook Profiles in registry
    $CommonSettings = $False

    # Determine Office Version and Set the Correct Paths
    $ol = New-Object -ComObject Excel.Application
    if ($ol.Version -eq "16.0") {
        $RegBase = "HKCU:\Software\Microsoft\Office\16.0\"    
    } elseif ($ol.Version -eq "15.0") {
        $RegBase = "HKCU:\Software\Microsoft\Office\15.0\"
    } else {
        Write-Error "The installed version of Office is not supported"
        exit 1
    }

    $Profiles = (Get-ChildItem "$($RegBase)Outlook\Profiles" -ea stop).PSChildName


    # Handle Single or Multiple
    If ($Null -eq $Profiles -or $Profiles.Count -ne 1) {
        Write-Host "Warning - Applying signature to all Outlook profiles" 
        $OutlookProfilePath = "$($RegBase)Common\MailSettings"
        $CommonSettings = $True
    } Else {
        # Path to default profile is elsewhere in the registry
        $OutLookProfilePath = "$($RegBase)Outlook\Profiles\" + $Profiles.Trim() + "\9375CFF0413111d3B88A00104B2A6676\00000001" 
    }

    # If we have an Outlook profile, check that we can match the User Principal name with the account name that's stored in the registry for Outlook ProPlus 
    # But only do this for now if we're not updating all profiles 
    If ($CommonSettings -eq $False) {
        $OutlookProfile = Get-ItemProperty -Path $OutLookProfilePath
        If ($OutlookProfile."Account Name" -ne $UserId) {
            $OutLookProfilePath = "$($RegBase)Outlook\Profiles\" + $Profiles.Trim() + "\9375CFF0413111d3B88A00104B2A6676\00000002"
            $OutlookProfile = Get-ItemProperty -Path $OutLookProfilePath
            If ($OutlookProfile."Account Name" -ne $UserId) {
                # We don't have a profile match
                Write-Host "Can't match signature and Office 365 user principal name"
                exit 1
            }
        }
    }


    # Make sure we have a company name

    # Construct a signature file in HTML format using the information fetched from Azure Active Directory
    $HeadingLine = "<HTML><HEAD><TITLE>Signature</TITLE><BODY><BR><table style=`"FONT-SIZE: 8pt; COLOR: gray; FONT-FAMILY: `'Segoe UI`' `"> <tr>"
    $ImageLine = "<td ><img src='" + $CompanyLogo + "' border='0'></td>"

    if ($UserProperties.Title) {
        $PersonLine = "<td padding='0'><B>" + $UserProperties.DisplayName + " </B> - " + $UserProperties.Title + "<br />"
    } else {
        $PersonLine = "<td padding='0'><B>" + $UserProperties.DisplayName + " </B><br />"
    }

    $CompanyLine = "<b>" + $CompanyName + "</b><br />"

    if ($UserProperties.StreetAddress) {
        $CompanyLine = $CompanyLine + $UserProperties.StreetAddress
    }

    if ($UserProperties.City) {
        $CompanyLine = $CompanyLine + ", " + $UserProperties.City
    }

    if ($UserProperties.PostalCode) {
        $CompanyLine = $CompanyLine + ", " + $UserProperties.PostalCode 
    }

    $CompanyLine = $CompanyLine + "<br />"

    if ($UserProperties.TelephoneNumber) {
        $CompanyLine = $CompanyLine + "T: $($UserProperties.TelephoneNumber)" + "<br />" 
    }

    if ($UserProperties.Mobile) {
        $CompanyLine = $CompanyLine + "M: $($UserProperties.Mobile)" + "<br />" 
    }

    $CompanyLine = $CompanyLine + "E: $UserId" + "<br /><br />" 

    $EndLine = "</td></tr></table><br /><br /></BODY></HTML>"

    If(!(test-path $LocalSignaturePath)) {
        New-Item -ItemType Directory -Force -Path $LocalSignaturePath
    }

    # Put everything together and output the HTML file
    $HeadingLine + $ImageLine + $PersonLine + $CompanyLine + $EndLine | Out-File $HtmlPath

    # Update the registry settings where Outlook picks up its signature information
    Get-Item -Path $OutlookProfilePath | New-Itemproperty -Name "New Signature" -value $SignatureName -Propertytype string -Force 
    Get-Item -Path $OutlookProfilePath | New-Itemproperty -Name "Reply-Forward Signature" -value $SignatureName -Propertytype string -Force 
   

} else {
    Write-Error "User not found"
    Exit 1
}


You may also like...