Trustwave Unveils New Offerings to Maximize Value of Microsoft Security Investments. Learn More

Trustwave Unveils New Offerings to Maximize Value of Microsoft Security Investments. Learn More

Managed Detection & Response

Eliminate active threats with 24/7 threat detection, investigation, and response.

Co-Managed SOC (SIEM)

Maximize your SIEM investment, stop alert fatigue, and enhance your team with hybrid security operations support.

Advisory & Diagnostics

Advance your cybersecurity program and get expert guidance where you need it most.

Penetration Testing

Test your physical locations and IT infrastructure to shore up weaknesses before exploitation.

Database Security

Prevent unauthorized access and exceed compliance requirements.

Email Security

Stop email threats others miss and secure your organization against the #1 ransomware attack vector.

Digital Forensics & Incident Response

Prepare for the inevitable with 24/7 global breach response in-region and available on-site.

Firewall & Technology Management

Mitigate risk of a cyberattack with 24/7 incident and health monitoring and the latest threat intelligence.

Offensive Security
Solutions to maximize your security ROI
Microsoft Exchange Server Attacks
Stay protected against emerging threats
Rapidly Secure New Environments
Security for rapid response situations
Securing the Cloud
Safely navigate and stay protected
Securing the IoT Landscape
Test, monitor and secure network objects
Why Trustwave
About Us
Awards and Accolades
Trustwave SpiderLabs Team
Trustwave Fusion Security Operations Platform
Trustwave Security Colony
Technology Alliance Partners
Key alliances who align and support our ecosystem of security offerings
Trustwave PartnerOne Program
Join forces with Trustwave to protect against the most advance cybersecurity threats
SpiderLabs Blog

TWSL2016-005: Memory corruption in a third-party component: how to find what’s wrong

In continuation of this post:

Recently we stumbled upon an issue in DevArt dotConnect for Oracle component (8.5.583): supplying an overly long Oracle Database username results in a crash with memory corruption message. This doesn't happen on every run however. We decided to investigate what goes on. First thing is writing simple minimal C# application that connects to Oracle Database to reproduce the issue.

Minimal C# app:


It will throw this exception:

Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Devart.Data.Oracle.a3.a(cl A_0, t A_1)
at Devart.Data.Oracle.OracleInternalConnection..ctor(cl connectionOptions, OracleInternalConnection proxyConnection)
at Devart.Data.Oracle.a1.a(ae A_0, Object A_1, DbConnectionBase A_2)
at Devart.Common.DbConnectionFactory.a(DbConnectionPool A_0, ae A_1, DbConnectionBase A_2)
at Devart.Common.DbConnectionPool.a(DbConnectionBase A_0)
at Devart.Common.DbConnectionPool.GetObject(DbConnectionBase owningConnection)
at Devart.Common.DbConnectionFactory.b(DbConnectionBase A_0)
at Devart.Common.DbConnectionClosed.Open(DbConnectionBase outerConnection)
at Devart.Common.DbConnectionBase.Open()
at Devart.Data.Oracle.OracleConnection.Open()
at Demo.Program.Main(String[] args)

From the stacktrace we know that possible candidate for examination Is Devart.Data.Oracle.a3.a(cl A_0, t A_1) since it's closest to the exception. The name suggests it is obfuscated but nevertheless we fire up Red-Gate Reflector on Devart.Data.Oracle.dll and navigate to that method. After simple lookup of username-setting OCI function references (namely we look for OCIAttrSet with OCI_ATTR_USERNAME parameter) we find a few occurrences of this pattern:

if (destination == IntPtr.Zero){ destination = Marshal.AllocHGlobal(100); } byte[] buffer4 = Utils.GetMaxBytes(encoding, cl.u(), out num); Marshal.Copy(buffer4, 0, destination, buffer4.Length); this.e(this.b.OCIAttrSet(this.g, 9, destination, num, 0x16, this.d)); // Constants 9 and 0x16 here are OCI_HTYPE_SESSION and OCI_ATTR_USERNAME respectively

From the above we see that a buffer of fixed size (100 bytes) is allocated from the heap no matter how long actual username is. Obviously it is a problem. Let's confirm what comes from Utils.>GetMaxBytes method for a string of 34 characters passed in UTF8 encoding:

public static byte[] GetMaxBytes(Encoding encoding, string s, out int byteCount) { int length = s.Length; byte[] bytes = new byte[encoding.GetMaxByteCount(length) + 1]; byteCount = encoding.GetBytes(s, 0, length, bytes, 0); return bytes; }

Simple program using this snippet results in 106 bytes – this means that subsequent Marshal.Copy will overwrite adjustent heap structures. Depending on what specific structures are overwritten it may lead to application crash, code execution (possible) or simply go unnoticed.

Now let's use WinDbg to confirm our finding: run the application under WinDbg debugger and issue the following commands to set a breakpoint on Marshal.AllocHGlobal:

.symfix sxe ld clrjit g .loadby sos clr !name2ee mscorlib.dll System.Runtime.InteropServices.Marshal.AllocHGlobal

Notice the MethodDesc values: select the one that corresponds to the AllocHGlobal(IntPtr) overload since the Int32 overload just wraps IntPtr one. use the next command to set a breakpoint on that value:

!bpmd -md 703b8a84

Resume execution and examine CLR stack on each break via:

!CLRStack -p

Eventually it will break on:

0:007> !CLRStack -p
OS Thread Id: 0x8f4 (7)
Child SP IP Call Site
05a8f7a4 70ad8a20 System.Runtime.InteropServices.Marshal.AllocHGlobal(IntPtr) PARAMETERS: cb (<CLR reg>) = 0x00000064 05a8f7a8 0061e238 Devart.Data.Oracle.a3.b(System.Object) PARAMETERS: this (0x05a8f7c4) = 0x02337b74 A_0 = <no data>

In blue we highlighted 100 bytes argument (0x64), in red our culprit method. Stepping out (Shift+F11) lands us in this disassembly:

// Marshal.AllocHGlobal 0061e233 e8e8a74b70 call mscorlib_ni+0x368a20 (70ad8a20) 0061e238 8945b4 mov dword ptr [ebp-4Ch],eax ss:002b:05a8f8b4=00000000 0061e23b 8b85c0feffff mov eax,dword ptr [ebp-140h] 0061e241 8b5014 mov edx,dword ptr [eax+14h] 0061e244 8d45c4 lea eax,[ebp-3Ch] 0061e247 50 push eax 0061e248 8b8db8feffff mov ecx,dword ptr [ebp-148h] 0061e24e ff15c40d6200 call dword ptr ds:[620DC4h] 0061e254 8945ac mov dword ptr [ebp-54h],eax 0061e257 ff75b4 push dword ptr [ebp-4Ch] 0061e25a ff7004 push dword ptr [eax+4] 0061e25d 8bc8 mov ecx,eax 0061e25f 33d2 xor edx,edx // Marshal.Copy 0061e261 e8ea67af70 call mscorlib_ni+0x9a4a50 (71114a50)

Now on 0061e261 examine the object pointed to by the ecx register with the do command:

0:007> !do ecx Name: System.Byte[] MethodTable: 70b44588 EEClass: 707d229c Size: 164(0xa4) bytes Array: Rank 1, Number of elements 152, Type Byte (Print Array) Content: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Fields: None

It is the source Byte array containing 164 elements. Register edx holds zero (second argument to the Copy), in esp there is 0xa4 (164 decimal) – source array length, at esp+4 is pointer to destination. Before the Marshal.Copy the destination is:

0:007> d poi(esp+4) 0033e458 0d f0 ad ba 0d f0 ad ba-0d f0 ad ba 0d f0 ad ba ................ 0033e468 0d f0 ad ba 0d f0 ad ba-0d f0 ad ba 0d f0 ad ba ................ 0033e478 0d f0 ad ba 0d f0 ad ba-0d f0 ad ba 0d f0 ad ba ................ 0033e488 0d f0 ad ba 0d f0 ad ba-0d f0 ad ba 0d f0 ad ba ................ 0033e498 0d f0 ad ba 0d f0 ad ba-0d f0 ad ba 0d f0 ad ba ................ 0033e4a8 0d f0 ad ba 0d f0 ad ba-0d f0 ad ba 0d f0 ad ba ................ 0033e4b8 0d f0 ad ba ab ab ab ab-ab ab ab ab ee fe ee fe ................ 0033e4c8 00 00 00 00 00 00 00 00-4c e7 9c 31 49 46 00 00 ........L..1IF..

After return from Marshal.Copy:

0:007> d 0033e458 0033e458 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 0033e468 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 0033e478 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 0033e488 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 0033e498 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 0033e4a8 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 0033e4b8 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 0033e4c8 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA

As we see it changed memory beyond allowed 100 bytes. Again, depending on what structures are corrupted we may observe different behavior of the app. This buffer overflow situation can be used to inject code via a well-crafted username or password string.


We see that the perils of using a fixed size buffer to hold a user-entered value still exist, even in a managed environment if it wraps native code. The problem is avoidable by checking the size of the data to be copied, which should be taken as common programming practice.

This blog post was co-authored by Martin Rakhmanov and Ernesto Cullen.

Latest SpiderLabs Blogs

Using AWS Secrets Manager and Lambda Function to Store, Rotate and Secure Keys

When working with Amazon Web Services (AWS), we often find that various AWS services need to store and manage secrets. AWS Secrets Manager is the go-to solution for this. It's a centralized service...

Read More

Facebook Malvertising Epidemic – Unraveling a Persistent Threat: SYS01

The Trustwave SpiderLabs Threat Intelligence team's ongoing study into how threat actors use Facebook for malicious activity has uncovered a new version of the SYS01 stealer. This stealer is designed...

Read More

Tips for Optimizing Your Security Operations Framework

Building an effective Security Operations framework that provides the right balance of people, processes, and technologies can take years.

Read More