[参考]Powershell简单入门
由于诸多样本都使用Powershell执行无文件攻击,加载本文可能会触发您的杀毒软件报告。本文不下载任何文件,不执行任何文件,请放心加载!!
于实战中应用Powershell
Powershell是一个基于.NET(包括.NET Core
(Powershell 6.0)、.Net Framework
)的基于任务的自动化命令行。简单地说,他比cmd和VBSript相比而言提供了更多功能,并且可以支持.NET代码,这意味着可以在脚本解释器中直接嵌入大量复杂代码。
Powershell作为无文件攻击的一种有效的手段,在诸多APT、商业攻击中被广泛使用。由于其强大的命令执行功能,Powershell的脚本可以做到规避部分AV、EDR等。[1]
作为批处理的一种方式,它提供了一种更加方便的方法来操作计算机中的诸多文件和操作,可以很大程度上自动化很多繁琐的操作。从维护计算机的方向上,我们可以通过编写powershell脚本对诸多任务实施自动化,类似于Python,其语法简单,系统自动支持,因此十分强大。
基于Powershell的真实样本列表(部分)
由于样本的危险性,这里不给出详细的样本获取链接。
Powershell脚本语言基础
杂项
-
注释:
#
# 在此处键入注释 <# 这里是块注释 #>
-
变量:
$
$var1 = $null #所有变量的初值都为$null ${var-1} = $null #含有特殊字符,需要使用大括号声明变量 ${D:\var} = 1 #将变量的值动态的保存到对应的文件内 [int]$var = 1 #指定变量类型 [ValidateRange(1,10)][int]$var = 1 #指定变量范围
-
打印变量:
- 直接输出:
$var
将直接输出变量内容 - 通过格式化(见下方):
echo ('var = {0}' -f $var)
- 直接输出:
-
删除变量:
Clear-Variable -Name var
或者clv var
-
查看变量类型:
$var.GetType()
-
作用域:
$[<scope-modifier>:]<name> = <value>
-
自动变量[5]
-
常用自动变量
-
$?
:上一次执行成功。成功返回True -
$$
:返回上一个指令的最后一个令牌(token)
$^
:返回第一个token例如
echo a b c $var_last = $$ # = c $var_first = $^ # = echo
-
$_
:常与Foreach-Object
配套使用,代表传入当前循环的token别名为
$PSItem
(token中包含当前对象)Foreach-Object也可以用%代替,例如
1,2,3 | %{ write-host $_ }
-
-
系统缺省变量
-
$args
:参数 -
$foreach
:循环计数器,用于快速确定foreach的循环次数 -
$MyInvocation.MyCommand.Path
:当前脚本的执行位置(包括文件名)$MyInvocation
是非常常用的变量,因此善用可以加快自动化的速度
-
字符串
全文本字符串
使用单引号'
包裹,在这个单引号内部的所有内容都不会被转义。
$var = 1
echo 'var = $var' #输出var = $var
软字符串
使用双引号"
包裹,这个双引号内部可以包含变量,自动格式化
$var = 1
echo "var = $var" #输出var = 1
Here-String
用于
- 多行
- 包括单双引号的字符串
- 帮助文本
@'
这里面的变量不会被转义
'@
$var = ”变量“
@"
这里面的$var会被转义
"@
杂项
-
拼接:
$s1 + $s2
也可以使用运算符完成上述操作
$s1,$s2 -join
或者利用.NET:[System.String]::Concat($s1,$s2)
-
子串:
$s1.SubString(3,5)
-
字符串格式化:
'{0} -f $var'
,注意这里格式化不需要考虑上述的单双引号规则 -
替换:
$s1.Replace('a','b')
数组
和变量声明相同
$k = @() #初始化空数组
$k = 1,2,3,4,5 #初始化五元数组
$k = ($k[0..3]) #删掉最后一个值
$k += 5 #增加一个项目
$k[-1] #显示最后一个项目
$k[2..4] #访问第三个到第五个项目
$k[-4..-1] #从后往前显示这些项目
数学运算符
-
+.-,*,/,%
常识无区别 -
比较
-eq # == -ne # != -gt # > -ge # >= -lt # < -le # <=
-
输出到
> # 将内容输出到文件 >> # 将内容追加到文件
输出到是个很复杂的运算符,参见微软文档。[6]
基础语句
If-Else/Do-While/Switch/For/While都和普通编程语言无异
ForEach
Foreach($<item> in $<collection>)
{
Statement-1
Statement-2
Statement-N
}
函数
常规函数
function [<scope:>]<name> [([type]$parameter1[,[type]$parameter2])]
{
param([type]$parameter1 [,[type]$parameter2])
dynamicparam {<statement list>}
begin {<statement list>}
process {<statement list>}
end {<statement list>}
}
高级函数
高级函数的区别是,高级函数可以像cmdlets一样调用,亦即
#普通函数
Function(0,1)
#高级函数
Advanced_Func 0 1
其实现比较复杂,使用的不多
function Send-Greeting
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string] $Name
)
Process
{
Write-Host ("Hello " + $Name + "!")
}
}
参见微软文档[7]
实用Powershell脚本示例
批量修改文件属性
$Path = Split-Path -Parent $MyInvocation.MyCommand.Path
$Files = Get-ChildItem -Path $Path
foreach ($File in $Files)
{
$Years = 2022
$Month = Get-Random -Minimum 1 -Maximum 12
$Day = Get-Random -Minimum 1 -Maximum 30
$Hours = Get-Random -Minimum 0 -Maximum 24
$Minutes = Get-Random -Minimum 0 -Maximum 60
$Seconds = Get-Random -Minimum 0 -Maximum 60
$NewTime = ("$Years-$Month-$Day {0}:{1}:{2}" -f $Hours,$Minutes,$Seconds)
$File.LastAccessTimeUtc = $NewTime
$File.LastWriteTimeUtc = $NewTime
$File.CreationTimeUtc = ("$Years-$Month-$Day {0}:{1}:{2}" -f $Hours,$Minutes,$Seconds)
if($File.Extension.ToString() -ne '.ps1')
{
$NewName = [guid]::NewGuid()
$File.MoveTo("$NewName.txt")
[guid]::NewGuid() > "$NewName.txt"
}
if((Get-Random -Maximum 100 -Minimum 5 ) -eq 40)
{
try {
$File.Delete()
}
catch {
Write-Host ('Error@delete {0}' -f $File.FullName) -ForegroundColor Red
}
}
$ErrorActionPreference = "Stop"
try {
throw 'Expected throable object!'
}
catch {
Write-Host ('Error@delete {0}' -f $Error[0].ErrorDetails) -ForegroundColor Yellow
}
}
获取本机的CIM管理类
$ErrorActionPreference = "Stop"
function Get-WmiNamespace{
Param ($Namespace='ROOT')
Try {
Get-WmiObject -Namespace $Namespace -Class __NAMESPACE | ForEach-Object{
($ns='{0}\{1}' -f $_.__NAMESPACE, $_.Name)
Get-WmiNamespace -Namespace $ns
}
}
Catch {
Write-Host ('Error@Namespace: {0} - {1}' -f $Namespace, $Error[0])
}
}
$WmiClasses = Get-WmiNamespace | ForEach-Object{
$Namespace = $_
Try {
Get-WmiObject -Namespace $Namespace -List | ForEach-Object{
'{0},{1}' -f $Namespace, $_.Path.ClassName
}
}
Catch {
Write-Host ('Error@Class: {0} - {1}' -f $Namespace, $Error[0])
}
} | Sort-Object -Unique
Set-Content -Path 'D:\WmiContents.txt' -Value $WmiClasses
从远端请求文件——powershell爬虫
这个就很简单啦,利用Invoke-RestMethod
的方法可以快速从互联网上下载文件
一个实例:使用 PowerShell 爬取妹子图网站 | 存在感消失的地方|ω•`) (akarin.dev)
下面是调用可断点续传的一个简单指令
$save_to = (Split-Path -Parent $MyInvocation.MyCommand.Path) + ('\7z.exe')
Start-BitsTransfer -Source 'https://www.7-zip.org/a/7z2201-x64.exe' -Destination $save_to
添加任务计划程序
$action = New-ScheduledTaskAction -Execute "C:\Windows\System32\cmd.exe" -Argument "-c echo hellworld > D:\hello.txt"
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date)
$principal = New-ScheduledTaskPrincipal -UserId "$env:ComputerName\$env:UserName" -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet
$task = New-ScheduledTask -Action $action -Principal $principal -Trigger $trigger -Settings $settings
Register-ScheduledTask T1 -InputObject $task
Invoke-Command/Invoke-Expression
伪装、隐藏所有脚本,广泛用于实战样本中。
下面的指令将会执行字符串内的指令
$cmd = Read-Host 'Please input a command'
Invoke-Expression $cmd
因此可以混淆
(一个假题)
$flag = 'fake_flag'
$binary =
@'
F0Ol^UJd^UJd`jSe_@?wV0Gm`ENHGCG._iOm_TSNVUOlFA-dHCKt_Di-IS?eaDddIS?e`jSra@>hQUiG_kWsV/C-^T6rIh.2N/6q_TCrW@2NVUOlHNlhOjipWUJdMP?EWUNqN/em_DOGaDSqF@.NVUOlF@OPaT2R^T.iRDC-^>lhaDiqWS6vWUK._ENdMP>t@jWs`jSeV/dlGBWm_DRd^T1dGBWm_DSwHNm4@f>dF@>haDiqWS6vWUK._ENdHw-dGBWm_DRrN0GiVUOm_/2R^T.iIh.m_Dum`/Sg_/2h@k-H^TVlGEOm_TSc`jSwaTu-F@.pa@>/J@hHbtldF@>d^TVlGEOm_TSc`jSwaTu-F@.ka@>0J@hHF@>dFEpHF@>dF@>dF@>hWjueWv>6F@aj_DCkb0GiVTucWjueW0-k@f>dF@>dF@>dF/Sg^D5dGDWpVT`HF@>dFE-HcN::
'@
#binary =
<#
I3RoaXMgaXMgcmVhbCBzY3JpcHQKJFJ1blRpbWVQYXRoID0gKFNwbGl0LVBhdGggLVBhcmVudCAkTXlJbnZvY2F0aW9uLk15Q29tbWFuZC5QYXRoKQokRmlsZXMgPSBHZXQtQ2hpbGRJdGVtIC1QYXRoICRSdW5UaW1lUGF0aAokdGltZV9yZXN1bHQgPSAwCmZvcmVhY2goJEZpbGUgaW4gJEZpbGVzKQp7CiAgICAkdGltZV9yZXN1bHQgKz0gJEZpbGUuQ3JlYXRpb25UaW1lLk1pbGxpc2Vjb25kCn0KaWYoJHRpbWVfcmVzdWx0IC1sdCA2MCkKewogICAgaWYoJHRpbWVfcmVzdWx0IC1ndCA3MCkKICAgIHsKICAgICAgICAkZmxhZyA9ICdmbGFne3JlYWxfZmxhZ30nCiAgICAgICAgI2VjaG8gJGZsYWcKICAgIH0KfQ==
#>
<#
#this is real script
$RunTimePath = (Split-Path -Parent $MyInvocation.MyCommand.Path)
$Files = Get-ChildItem -Path $RunTimePath
$time_result = 0
foreach($File in $Files)
{
$time_result += $File.CreationTime.Millisecond
}
if($time_result -lt 60)
{
if($time_result -gt 70)
{
$flag = 'flag{real_flag}'
#echo $flag
}
}
#>
#[int]$i =0
#for ($i = 0; $i -lt $binary.Length; $i++)
#{
# $c = $binary[$i]
# [int][char]$c = [int][char]$c - 3
# $c = [char][int]$c
# [char] $k = [char]$c
# Write-Host "$k" -NoNewline
#}
附录
作者发布、转载的任何文章中所涉及的技术、思路、工具仅供以安全目的的学习交流,并严格遵守《中华人民共和国网络安全法》、《中华人民共和国数据安全法》等网络安全法律法规。
任何人不得将技术用于非法用途、盈利用途。否则作者不对未许可的用途承担任何后果。
本文遵守CC BY-NC-SA 3.0协议,您可以在任何媒介以任何形式复制、发行本作品,或者修改、转换或以本作品为基础进行创作
您必须给出适当的署名,提供指向本文的链接,同时标明是否(对原文)作了修改。您可以用任何合理的方式来署名,但是不得以任何方式暗示作者为您或您的使用背书。
同时,本文不得用于商业目的。混合、转换、基于本作品进行创作,必须基于同一协议(CC BY-NC-SA 3.0)分发。
如有问题, 可发送邮件咨询.