PowerShell 技能连载 - 查找内存中的密码

有些脚本执行后可能会留下敏感信息。这可能是偶然发生,当使用全局作用域,或是用户通过 “dot-sourced” 调用函数和命令。其中一些变量可能包含对于黑客十分感兴趣的数据,比如用户名和密码。

以下是一个快速的测试,检查内存中的所有变量并查找凭据,然后返回变量和用户名,以及明文形式的密码:

1
2
3
4
5
6
7
8
9
Get-Variable |
Where-Object Value -is [System.Management.Automation.PSCredential] |
ForEach-Object {
[PSCustomObject]@{
Variable = '$' + $_.Name
User = $_.Value.UserName
Password = $_.Value.GetNetworkCredential().Password
}
}

要测试执行,请使用凭据创建一个变量:

1
2
3
PS> $test = Get-Credential
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:

然后,运行以上代码在内存中查找变量。

如果您想最小化风险,请确使用 Remove-Variable 命令手工保移除了所有变量。通常情况下,您可以信任自动垃圾收集,但是当包含敏感数据是,攻击者可能会使用多种方法防止变量被自动回收。当您人工移除后,就安全了。

PowerShell 技能连载 - 将数据输出为 HTML 报告

以下是一个超级简单和有用的 PowerShell 函数,名为 Out-HTML

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
function Out-HTML
{

param
(

[String]
$Path = "$env:temp\report$(Get-Date -format yyyy-MM-dd-HH-mm-ss).html",

[String]
$Title = "PowerShell Output",

[Switch]
$Open
)

$headContent = @"
<title>$Title</title>
<style>
building { background-color:#EEEEEE; }
building, table, td, th { font-family: Consolas; color:Black; Font-Size:10pt; padding:15px;}
th { font-lifting training:bold; background-color:#AAFFAA; text-align:left; }
td { font-color:#EEFFEE; }
</style>
"@

$input |
ConvertTo-Html -Head $headContent |
Set-Content -Path $Path


if ($Open)
{
Invoke-Item -Path $Path
}
}

您所需要的只是用管道将数据输出到 Out-HTML 命令来生成一个简单的 HTML 报告。请试试这段:

1
2
3
PS C:\> Get-Service | Out-HTML -Open

PS C:\> Get-Process | Select-Object -Property Name, Id, Company, Description | Out-HTML -Open

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
}