Skip to main content

Command Palette

Search for a command to run...

The Silent Recon - Why Malware Authors Choose RID Cycling

Updated
8 min readView as Markdown
The Silent Recon - Why Malware Authors Choose RID Cycling

When an attacker lands a foothold on a Windows machine, their absolute first priority is discovery. They need to answer one crucial question: Who else is on this box?

Finding local accounts—especially local administrators—is pure gold for an adversary. If a local admin account shares a username and password across multiple systems, that single misconfiguration lets an attacker pivot from one compromised machine to the entire network in minutes.

But how an attacker finds these accounts makes all the difference between triggering alarms or slipping by completely unnoticed.


The Loud Way: Why net user Gets You Caught

If you look up how to list users on Windows, you’ll usually find these common methods:

  1. The Old WAY OF Command Line: Running net user or net localgroup administrators.

  2. PowerShell / .NET / ADSI: Running commands like Get-LocalUser.

  3. WMI Queries: Using wmic useraccount list brief.

Why Security Tools Trap This Instantly

Modern Endpoint Detection and Response (EDR) software acts like a hyper-alert security guard watching the command line.

The moment a non-standard background process (like an exploited app or a malicious document) suddenly spawns cmd.exe /c net user, the EDR flags it immediately. Security teams have written strict rules specifically to trigger alerts whenever these exact command-line footprints show up in the logs.

Because these standard tools leave an obvious trail, smart malware authors completely avoid them. Instead, they use a technique that creates zero new processes and leaves no command-line footprints: RID Cycling.

The same discovery goal, two very different footprints — and exactly what EDR records for each path.


Understanding SIDs and RIDs

To understand how RID cycling works, we first need to look at how Windows actually handles identities. In the eyes of Windows, absolutely everything—each user, computer, group, and even the domain itself—is treated as an object. And every single object gets its own unique digital ID card called a SID (Security Identifier).

A SID string is split into two primary parts: the Base SID (which identifies the authority/source) and the RID (Relative Identifier) (the unique sequential number at the very end).

One SID, taken apart: the Base SID stays fixed while only the RID at the tail changes from account to account.

A common point of confusion is how a machine tracks its own identity when it joins a network. The truth is, a domain-joined computer actually manages two completely separate identities running side-by-side:

The Blueprint of Windows IDs

  • The Machine Identity:

    • Before joining a domain: The computer uses its own randomly generated Machine SID.

    • While joining a domain: Active Directory hands it a brand-new identity built as [Domain SID] + [Machine RID].

    • The Secret: The machine never merges these. It keeps both.

  • User Identities:

    • Local User: Built using [Original Machine SID] + [User RID]. (It completely ignores the domain).

    • Domain User: Built using [Domain SID] + [User RID].

  • Group Identities:

    • Local Group (Custom): Built using [Original Machine SID] + [Group RID].

    • Domain Group: Built using [Domain SID] + [Group RID].

Two worlds of identity. Every object is simply its authority’s SID plus a RID — only the authority (LOCAL vs DOMAIN) changes.

Wait, Where is that Domain Computer SID Actually Used?

If the machine keeps that [Domain SID] + [Computer RID] identity, where does it use it? Think of it as a Corporate Employee ID Badge.

During system boot, way before the login screen appears and before any human enters a password, lsass.exe starts up. The computer logs into the Domain Controller using its hidden machine account password. It flashes its Domain Computer SID to prove it is a legitimate corporate asset. This lets it pull down Group Policies and safely communicate server-to-server over the network.

The machine’s domain identity is a boot-time corporate badge — and it never touches the local Machine SID that RID cycling abuses.


Test It Yourself: Checking Machine's SIDs

You can see this identity mapping in action right now using a standard Command Prompt (cmd.exe):

  1. View your current user account's SID:

    whoami /user
    

    Look at the output. The long middle part is your unique Machine SID, and the very last number (like -1001) is your User RID.

  2. View your local groups:

    whoami /groups
    

    You might notice groups like BUILTIN\Administrators look like S-1-5-32-544. They don't use your Machine SID because they are Built-in System Groups. Windows hardcodes these globally with a generic prefix (S-1-5-32) so they are the same on every PC in the world.

  1. Prove the rule (Create a Custom Group):
    If you run an elevated Command Prompt and create a custom group, Windows will immediately fall back to your unique Machine SID:

    net localgroup MyTestGroup /add
    powershell -Command "Get-LocalGroup -Name MyTestGroup | Select-Object Name, SID"
    

    Output: MyTestGroup S-1-5-21-[Your-Machine-SID]-1002

The Guessing Game: How RID Cycling Works

Now that we have the mental model down, RID cycling is easy to understand.

Instead of knocking on the front desk and asking for a full list of all employees (which security will flag as suspicious), an attacker walks up to an automated intercom and manually dials extensions one by one: 1001, 1002, 1003. If a name pops up, they write it down. If it says "Invalid," they skip it.

An attacker throws this logic into an automated loop:

  1. Grab the Base Machine SID: They grab the local computer's unique Machine SID (which they can easily read from basic registry paths like ProfileList).

  2. Build the Guess List: They programmatically append sequential RIDs to the end of that Machine SID.

    • They start with well-known ones: Machine-SID-500 (Always the built-in Administrator).

    • Then they cycle through user space: Machine-SID-1000, Machine-SID-1001, Machine-SID-1002...

  3. Ask for a Translation: For every SID they generate, they call a standard Windows utility function: LookupAccountSid.

Windows looks at the generated SID and answers: "Oh, that ID belongs to a user named Veera." If the RID doesn't exist, Windows just returns an error, and the script moves to the next number.

The loop in one picture: hold the Base SID fixed, walk each RID, and let Windows translate the ones that exist.

# Step 1: Extract the local Machine SID directly from the ProfileList registry key
# We find any subkey starting with 'S-1-5-21-', pick the first one, and strip off the final user RID.

$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
$UserSIDSubkey = (Get-ChildItem $RegPath | Where-Object { $_.PSChildName -like "S-1-5-21-*" } | Select-Object -First 1).PSChildName

if (-not $UserSIDSubkey) {
    Write-Error "[-] Could not find any local user SIDs in the ProfileList registry path."
    exit
}

$MachineSID = $UserSIDSubkey -replace '-[0-9]+$', ''
Write-Host "[+] Discovered Local Machine SID via Registry: $MachineSID" -ForegroundColor Green
Write-Host "[+] Starting RID Cycling loop..." -ForegroundColor Cyan
Write-Host "--------------------------------------------------"

# Step 2: Define the RIDs we want to test

# 500 = Admin, 501 = Guest, 1000+ = Custom Users
$RIDsToTest = @(500, 501) + (1000..1050)

# Step 3: Run the translation loop

foreach ($RID in $RIDsToTest) {
    # Construct the full target SID string
    $TargetSIDString = "$MachineSID-$RID"

    try {
        # Call the underlying Windows translation engine (LookupAccountSid equivalent)
        $SIDObject = New-Object System.Security.Principal.SecurityIdentifier($TargetSIDString)
        $Account = $SIDObject.Translate([System.Security.Principal.NTAccount])     
        # If it successfully translates, print out the account name
        Write-Host "[FOUND] RID $RID -> $($Account.Value)" -ForegroundColor Yellow
    }
    catch {
        # If the RID doesn't exist on the system, Windows throws an error. We silently skip it.
        continue
    }
}

Why This Completely Evades EDR

This technique turns a dangerous search problem into an innocent translation problem, completely blinding security filters.

  1. Registry Reads are Too Noisy to Log
    The ProfileList registry key is read constantly by legitimate applications to check user paths. Because EDR tools focus heavily on registry writes (to prevent malware persistence), logging every simple read operation of a standard system key would generate millions of useless logs and crush system performance. Security tools deliberately skip this background noise.

  2. SID Translation Mimics 100% Normal Behaviour
    Legitimate Windows components constantly call LookupAccountSid every time they render file properties, display permissions, or process logs. To an EDR agent, an in-process script translating a few hundred SIDs looks identical to normal operating system behaviour. There are no suspicious process spawns, no noisy command-line flags, and no restricted administrative APIs called.


20 views
N

Such a clean approach to security layers. Have you experimented with adding multi-factor or token-based validation here to make it even more robust?