Search This Blog

Feb 12, 2026

PowerShell - Beware What Your Function Returns

 Your PowerShell script could return something totally different from what you would have thought. Considering below function/script:

function demo-returnValues {
    "1"
    "2"
    return [string]"3"
}

$dn = demo-returnValues
write-host "return Type: $($dn.gettype().fullname)"
write-host "element count $($dn.count)"
write-host " Values:"
$dn|foreach {write-host "[[$_]]"}

It doesn't return a string of value "3"! Rather it returns an array of 3 elements. See its output:

return Type: System.Object[]
element count 3
Values:
[[1]]
[[2]]
[[3]]

This is because anything within a function that emits value that are not caught will be added to a pipeline. What you intend to return is also added to this pipeline. Finally it is this pipeline that gets returned.


 To fix this, you can do one of below 3

  1. Assigned to variable
  2. Suppressed by [void]
  3. piped to out-null
Revised function below. This returns only a string of "4"

function demo-returnValues {
    [void]"1"
    $uselessVar = "2"
    "3" | out-null
    return [string]"4"
}


Nov 7, 2025

Kerberoasting simple facts

 

Prerequisites for possible attack

  1. Attacker already possess an account in domain
  2. Attacker has access to KDC
  3. Targeted account must have SPN

 

Attack path:

  1. Attacker logs in with account A
  2. Attacker request TGS against account B that has SPN, using SPN to obtain ticket
  3. Attacker dumps the ticket and crack it offline
  4. Attacker knows password of user B

 

Prevention:

  1. Strong passwords
  2. Disable RC4 encryption support for Kerberos tickets (this can be done on DC side and/or user account side)
    1. On DCs, use GPO to disable RC4 support “Security Options -> Network security: Configure encryption types allowed for Kerberos”
    2. On user account, attribute msDS-SupportedEncryptionsTypes
  3. Normal account should NOT have SPNs
  4. Use gMSA so password is random and strong

 

Detection:

  1. Spikes in EventID 4769 for same SPN
  2. Spikes in EventID 4769 from a normal user account

Jun 26, 2025

Entra ID extension attributes

 There are 4 types of extension attributes

  1. Extension attribute 1-15. This is a legacy borrow from on-prem extension attribute introduced by Exchange
  2. Directory Extension (tied to an application, but can be consumed by other applications)
  3. Schema Extension (tenant-wide)
  4. Open Extension
Please see https://learn.microsoft.com/en-us/graph/extensibility-overview

How to include "directory extension attribute" (type #2 above) in claims

  1. need to use Graph API to create claim mapping policy
  2. use below POST command and  JSON body of the Graph call

POST https://graph.microsoft.com/v1.0/policies/claimsMappingPolicies
{
  "definition": [
    "{ 
      \"ClaimsMappingPolicy\": {
        \"Version\":1,
        \"IncludeBasicClaimSet\":\"true\",
        \"ClaimsSchema\": [
          {
            \"Source\":\"user\",
            \"ID\":\"extension_hostingAppID_deviceID\",
            \"JwtClaimType\":\"deviceID\"
          }
        ]
      }
    }"
  ],
  "displayName": "IncludeDeviceID",
  "isOrganizationDefault": false
}
  1. Make a note of returned policy ID for steps followed
  2. make a POST call as below to assgin the policy to consuming app
command: POST 
https://graph.microsoft.com/v1.0//servicePrincipals/{id}/claimsMappingPolicies/$ref

            where ID is objectID of SPN 

      Body
      {
        "@odata.id": "https://graph.microsoft.com/v1.0/policies/claimsMappingPolicies/policyID"
      } // where id is policy ID
    1. Pay attention to different GUID used. In the actual policy, appID of hosting app is used(remove dashes); In POST command, objectID of consuming app is used
    2. Last step, enable app to accept custom claim
    PATCH https://graph.microsoft.com/v1.0/applications/{objID of app}
    Content-type: application/json

    {
      "api": {
        "acceptMappedClaims": true,
        "requestedAccessTokenVersion": 2
      }
    }