PowerShell 技能连载 - 从 PowerShell 函数中窃取敏感数据

PowerShell 函数常常处理敏感的信息,例如包含密码的登录信息,并且将这些信息存储在变量中。让我们假设有这样一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Connect-Server
{
param
(
[Parameter(Mandatory)]
[pscredential]
[System.Management.Automation.Credential()]
$Credential
)

"You entered a credential for {0}." -f $Credential.UserName
# now you could do something with $Credential
}

当运行这个函数时,会弹出提示输入凭据和密码。您的信息在函数中体现在 $Credential 变量中,并且当函数完成时,内部信息自动从内存中移除:

1
2
3
4
5
6
PS> Connect-Server -Credential tobias
You entered a credential for Tobias.

PS> $Credential

PS>

然而,任何攻击者都可以通过 dot-sourced 方式运行该函数。当函数执行完毕之后,所有内部变量都还在内存中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PS> . Connect-Server -Credential tobias
You entered a credential for Tobias.

PS> $Credential

UserName Password
-------- --------
Tobias System.Security.SecureString



PS> $Credential.GetNetworkCredential().Password
test

PS>

如您所见,攻击者现在可以访问凭据对象,并且也可以看到原始的密码。

要防止这种情况,您可以再次从内存中手动移除敏感的数据。您可以用 Remove-Variable 移除变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Connect-Server
{
param
(
[Parameter(Mandatory)]
[pscredential]
[System.Management.Automation.Credential()]
$Credential
)

"You entered a credential for {0}." -f $Credential.UserName
# now you could do something with $Credential, i.e. submit it to
# other cmdlets that support the -Credential parameter
# i.e.
# Get-WmiObject -Class Win32_BIOS -ComputerName SomeComputer -Credential $Credential

Remove-Variable -Name Credential
}

现在不再能获取到变量:

1
2
3
4
5
6
7
8
PS> Remove-Variable -Name Credential

PS> . Connect-Server -Credential tobias
You entered a credential for Tobias.

PS> $credential

PS>

PowerShell 技能连载 - 用参数的方式解决凭据

凭据是包含用户名和加密的密码的对象。如果您的 PowerShell 函数可以接受凭据,那么加上 PSCredential 类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Connect-Server
{
param
(
[Parameter(Mandatory)]
[pscredential]
$Credential
)

"You entered a credential for {0}." -f $Credential.UserName
# now you could do something with $Credential, i.e. submit it to
# other cmdlets that support the -Credential parameter
# i.e.
# Get-WmiObject -Class Win32_BIOS -ComputerName SomeComputer -Credential $Credential

}

当您运行以上代码后,再调用您的函数,将会显示一个对话框,显示用户名和密码。当指定一个用户名时,情况也是一样:也是打开一个对话框提示输入密码,然后将您的输入转换为一个合适的 PSCredential 对象:

1
PS> Connect-Server -Credential tobias

这个自动转换过程只在 PowerShell 5.1 及以上的版本中有效。在之前的 PowerShell 版本中,您可以先传入一个凭据对象。要在旧版的 PowerShell 中也启用该转换过程,您可以可以增加一个额外的转换属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Connect-Server
{
param
(
[Parameter(Mandatory)]
[pscredential]
[System.Management.Automation.Credential()]
$Credential
)

"You entered a credential for {0}." -f $Credential.UserName
# now you could do something with $Credential, i.e. submit it to
# other cmdlets that support the -Credential parameter
# i.e.
# Get-WmiObject -Class Win32_BIOS -ComputerName SomeComputer -Credential $Credential

}

PowerShell 技能连载 - 解析映射驱动器

是否想知道网络驱动器背后的原始 URL?以下是一个简单的 PowerShell 方法:

1
2
3
4
5
6
7
8
9
# make sure the below drive is a mapped network drive
# on your computer
$mappedDrive = 'z'


$result = Get-PSDrive -Name $mappedDrive |
Select-Object -ExpandProperty DisplayRoot

"$mappedDrive -> $result"

PowerShell 技能连载 - 用 BitsTransfer 在后台下载数据

下载大型的文件是一个挑战,因为下载的过程可能比机器的开机时间还长。通过 BitsTransfer,您可以在后台下载文件,甚至当机器关机或重启后,仍然能够续传直到完成。

以下代码可以下载一段 NASA 的视频。然而,下载过程是在后台,而且你可以继续做别的事。实际上。当您关闭 PowerShell 甚至电脑之后,下次启动时下载仍会继续。

1
2
3
4
$url = 'https://mars.nasa.gov/system/downloadable_items/41764_20180703_marsreport-1920.mp4'
$targetfolder = "$Home\Desktop"

Start-BitsTransfer -Source $url -Destination $targetfolder -Asynchronous -Priority Low

您需要做的只是偶尔查看一下 BitsTransfer,检查下载任务是否完成。当一个下载过程完成后,需要您负责完成下载。只有这样,所有下载的文件才会从后台缓冲区中复制到最终的目的地:

1
2
3
Get-BitsTransfer |
Where-Object Jobstate -eq Transferred |
Complete-BitsTransfer

完成。

要获取挂起的 BitsTransfer 任务的更多信息,请试试这段代码:

1
PS> Get-BitsTransfer | Select-Object -Property *

And if you have Admin privileges, you can even dump the jobs from other users, including internal accounts. This way you’ll see updates and other things that are on their way to you:
如果您有管理员特权,您甚至可以从其它用户那儿转储所有任务,包括内部账号。通过这种方式您可以查看进度等其它信息:

1
PS> Get-BitsTransfer -AllUsers

PowerShell 技能连载 - 通过 SSL 和 BitsTransfer

有一个很实用的内置方法可以下载文件,甚至支持 SSL 连接。该方法是 Start-BitsTransfer。它也会显示一个进度条,指示实际的下载状态。以下是一个可用的例子:

1
2
3
4
5
$url = 'https://mars.nasa.gov/system/downloadable_items/41764_20180703_marsreport-1920.mp4'
$OutFile = "$home\desktop\videoNasa2.mp4"


Start-BitsTransfer -Source $url -Destination $OutFile -Priority Normal -Description 'NASA Movie'

PowerShell 技能连载 - 通过 SSL 和 Invoke-WebRequest 下载数据

Invoke-WebRequest 可以下载文件,但是对 HTTPS URL 可能会遇到麻烦。要使用 SSL 连接,您可能需要改变缺省的设置。以下是一个可用的示例:

1
2
3
4
5
6
$url = 'https://mars.nasa.gov/system/downloadable_items/41764_20180703_marsreport-1920.mp4'
$OutFile = "$home\desktop\video.mp4"

$AllProtocols = [Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
[Net.ServicePointManager]::SecurityProtocol = $AllProtocols
Invoke-WebRequest -OutFile $OutFile -UseBasicParsing -Uri $url

PowerShell 技能连载 - 查找禁用的 GPO

以下是一行可以转储所有禁用了所有设置的组策略对象的代码:

1
Get-Gpo -All | Where-Object GpoStatus -eq AllSettingsDisabled

这个示例需要 Microsoft 免费的 RSAT 工具。

PowerShell 技能连载 - 浏览所有的事件日志

Get-EventLog 总是需要您通过 LogName 明确地指定一个事件日志。您无法使用通配符,并且无法一次性浏览所有事件日志。

然而,可以使用这个技巧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS> Get-EventLog -LogName *

Max(K) Retain OverflowAction Entries Log
------ ------ -------------- ------- ---
20.480 0 OverwriteAsNeeded 13.283 Application
512 7 OverwriteOlder 98 Dell
20.480 0 OverwriteAsNeeded 0 HardwareEvents
512 7 OverwriteOlder 0 Internet Explorer
512 7 OverwriteOlder 46 isaAgentLog
20.480 0 OverwriteAsNeeded 0 Key Management Service
128 0 OverwriteAsNeeded 97 OAlerts
10.240 0 OverwriteAsNeeded 0 PowerShellPrivateLog
512 7 OverwriteOlder 0 PreEmptive
Security
20.480 0 OverwriteAsNeeded 5.237 System
16.384 0 OverwriteAsNeeded 20 TechSmith
15.360 0 OverwriteAsNeeded 10.279 Windows PowerShell

所以显然,-LogName 终究不支持通配符。然而,您现在看到的不再是事件日志项,而是一个摘要视图。不过您仍然可以访问以下的事件日志条目:

1
PS> Get-EventLog -LogName * | Select-Object -ExpandProperty Entries -ErrorAction SilentlyContinue

这将从所有的日志中转储所有事件日志条目。在这儿,您可以添加自定义过滤器。要查看近 48 小时所有事件日志错误,请试试这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# take events not older than 48 hours
$deadline = (Get-Date).AddHours(-48)

Get-EventLog -LogName * |
ForEach-Object {
# get the entries, and quiet errors
try { $_.Entries } catch {}
} |
Where-Object {
# take only errors
$_.EntryType -eq 'Error'
} |
Where-Object {
# take only entries younger than the deadline
$_.TimeGenerated -gt $deadline
}

PowerShell 技能连载 - 创建事件日志报告

您可能经常使用 Get-EventLog 来转储事件日志信息,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS> Get-EventLog -LogName System -EntryType Error -Newest 6

Index Time EntryType Source InstanceID Message
----- ---- --------- ------ ---------- -------
5237 Jul 31 12:39 Error DCOM 10016 The des...
5234 Jul 31 09:54 Error DCOM 10016 The des...
5228 Jul 31 09:46 Error DCOM 10016 The des...
5227 Jul 31 09:40 Error DCOM 10016 The des...
5218 Jul 31 09:38 Error DCOM 10016 The des...
5217 Jul 31 09:38 Error DCOM 10016 The des...



PS>

但是,如果您想创建有用的报告,请确保将输出表格格式化,并启用换行:

1
PS> Get-EventLog -LogName System -EntryType Error -Newest 6 | Format-Table -AutoSize -Wrap

您现在可以方便地将结果输送到 Out-File 并创建有意义的文本报告。同时,设置其 Width 参数,以调整报告文件的宽度。

如果您不知道某个日志的确切名字,只需要将 "*" 赋给 -LogName

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS> Get-EventLog -LogName *

Max(K) Retain OverflowAction Entries Log
------ ------ -------------- ------- ---
20.480 0 OverwriteAsNeeded 13.283 Application
512 7 OverwriteOlder 98 Dell
20.480 0 OverwriteAsNeeded 0 HardwareEvents
512 7 OverwriteOlder 0 Internet Explorer
512 7 OverwriteOlder 46 isaAgentLog
20.480 0 OverwriteAsNeeded 0 Key Management Service
128 0 OverwriteAsNeeded 97 OAlerts
Security
20.480 0 OverwriteAsNeeded 5.237 System
15.360 0 OverwriteAsNeeded 10.279 Windows PowerShell

PowerShell 技能连载 - PowerShell 控制台的键盘快捷方式

PowerShell 控制台从 5.0 版开始发布了一个名为 PSReadLine 的模块,它不仅可以对命令做语法着色,还有更多的功能。它包含持久化的命令历史,并且可以将自定义命令绑定到键盘快捷方式上。

请看这个示例:

1
2
3
4
5
Set-PSReadlineKeyHandler -Chord Ctrl+H -ScriptBlock {
Get-History |
Out-GridView -Title 'Select Command' -PassThru |
Invoke-History
}

当您在 PowerShell 控制台中运行这段代码(它不能在 PowerShell ISE 中运行!),按下 CTRL + H 打开一个网格视图窗口,这个窗口中列出了所有命令行历史。您可以轻松地选择一个命令并执行它。

显然,这不仅是一个示例。您可以将任何脚本块绑定到未使用的键盘快捷方式,例如提交变更到 Git,或是打开喜爱的滚动新闻条。