CVE-2011-1206. IBM Tivoli LDAP

Some days ago, as a part of the Metasploit Bounty Program I decided to give a shot to one of the big ones, the IBM Tivoli LDAP vulnerability.

After poking around in the direction indicated in the advisory, I think it may not be exploitable after all.


Official description from the advisory


Stack-based buffer overflow in the server process in ibmslapd.exe in IBM Tivoli Directory Server (TDS)[…] and 6.3 before (aka allows remote attackers to execute arbitrary code via a crafted LDAP request.


Detailed description


The specific flaw exists in how ibmslapd.exe handles LDAP CRAM-MD5 packets. ibmslapd.exe listens by default on port TCP 389. When the process receives an LDAP CRAM-MD5 packet, it uses libibmldap.dll to handle the allocation of a buffer for the packet data. A specially crafted packet can cause the ber_get_int function to allocate a buffer that is too small to fit the packet data, causing a subsequent stack-based buffer overflow. This can be leveraged by a remote attacker to execute arbitrary code under the context of the SYSTEM user.


My notes


The application makes use of Smartheap for SMP (SHSMP.dll) from the company MicroQuill (see links below)

It’s a *CUSTOM HEAP MANAGER* and got our attention when it was called by libibmldap!ber_bvecfree+0x28 and caused the crash.


03c8fcfc 0a90ae5c SHSMP!MemFreePtr+0x10

03c8fd08 005b86d8 SHSMP!shi_AfxFreeMemoryDebug+0x1c

03c8fd1c 004098c8 libibmldap!ber_bvecfree+0x28[…]


So let’s take a look at the *infamous* ber_get_int() cross references…


This memmove() looked very promising so I spent some time checking all paths from ber_get_int to this memmove() and checking if I could control the Size parameter in order to produce an overflow. Unfortunately it resulted in a dead end.

Since there is already a patch for this (probably) vulnerable version I decided to do some binary diffing to narrow the scope, the same way it’s done with Microsoft patches.


Time to do some (turbo)diffing



Turbodiff yielded the following results:


1. ber_skip_tag() ??? red block below ADDITIONAL (?) cmp ecx, eax – jb loc_1002807B *after* ntohl() call

2. sub_1000E0E0() *after* the call to sub_1001BA90() there’s a cmp esi, 0xFFFFFFFF – jnz short loc_1000E3B0

3. sub_10019DA0() just deleted some debugging…

4. sub_1001C460() just some reordering of blocks…

5. sub_1001cae0() reordering and debugging elimination…

6. sub_10025a30() reverse condition, block reordering…


The patched libibmldap.dll contains an additional check in the function ber_skip_tag(), where the result of the call to ntohl (argument user controlled) is inspected. You can tell this is defined as an unsigned integer by the instruction used in the comparison (jb).

In this screenshot the additional block is marked in green:


The function returns the value stored in edi and copy the return value of ntohl (user controlled) at [ebp+0]

What happens with this value is too complicated to analyze statically, so let’s debug from here on :)


Immunity Debugger to the rescue


Once inside ber_skip_tag():

Set breakpoints at

  • nthol()
  • mov dword ptr ss:[ebp], eax
  • retn


nthol called with arg. 0x3A010000 -> obviously returns 0x0000013A

– at the time, edi equals 0x00AB0190 (esi and edi are interchanged, this is normal). That means that the missing check is calculating something based on a data structure: [esi + 8] – [esi +4], looks a lot like “end – start”

[edi + 4] = 0x00A5C630

[edi + 8] = 0x00A5C761

[edi + 8] – [edi + 4] = 0x0131 (goes into ecx)

cmp ecx, eax

jb short pop_and_return

translates to:

if ecx < eax failure();

since eax = 0x0131, there’s no problem this time :)


But… can I manipulate these bytes? What do they mean?

Maybe they are addresses in memory (from an allocation?)


EDI points to our user controlled data (mangled?).

Maybe some kind of (prev. chunk)(next chunk) structure?


Although promising, it looked like I wasn’t going nowhere. So I decided to follow the wise saying use the source, Luke and started the X-rays decompiler inside IDA.

It’s always easier to follow how the arguments propagate between function calls in the code than in the raw assembly :)


Following our logic the security problem must occur *after* the missing check, so we can focus on the code after that point in ber_skip_tag().

After inspecting the code produced by X-Rays and how the (unchecked) value gets propagated backwards through the function call chain it was clear that it wasn’t used in any dangerous function.

Time to forget the advisory and study what happen with the packet data from ber_scanf_w() downwards.


Independent study


From the ber_scanf() Linux Man Page:

The ber_scanf() routine is used to decode a BER element in much the same way that scanf() works. It reads from ber, a pointer to a BerElement such as returned byber_get_next(), interprets the bytes according to the format string fmt, and stores the results in its additional arguments. The format string contains conversion specifications which are used to direct the interpretation of the BER element. The format string can contain the following characters:


Sequence of octet strings with lengths. A struct berval *** should be supplied, which upon return points to a dynamically allocated null-terminated array of struct berval *’s containing the octet strings and their lengths. NULL is returned if the sequence is empty. The caller should free the returned structures using ber_bvecfree().


From the MSDN Library:

Use a berval structure for attributes that contain raw binary data, such as certificates, graphics, or sound files.

… Or maybe some MD5 hash :)


It looks like the problem isn’t restricted to the CRAM-MD5 authentication, but is generic when working with non-ascii data.


I set a breakpoint at ber_scanf() and checked the arguments…


The first argument of this function is a pointer to the BerElement (shown in the dump).

The second is the format string. In this case {V} indicating some non-ascii data (see above)

The third argument in this case is a struct berval*** (see above). You can dereference it twice to get to the struct berval* (containing pairs len/value)

Notice how the lengths are specified by the user!


typedef struct berval {
ULONG bv_len;
PCHAR bv_val;


These structures can be seen in the following screenshot:


The first pBerVal has a length of 0x08 bytes and is located at 0x00A904E0. It’s just the “CRAM-MD5” string on the original packet.

Following pairs len/value contain some of the pattern we sent (user controlled)


[17:47]:/pentest/exploits/framework3/tools/pattern_offset.rb a1Aa 2000


Nice, but are these values being used somewhere afterwards? Memory allocation? String copy?


In order to find out quickly I put a memory breakpoint on this page and breakpoints at malloc(), calloc(), realloc(), memset(), memcpy(), memmove() and strcpy() as well, just to be sure.


Notice how the heap manipulation functions (malloc, calloc, etc.) are hijacked into the SmartHeap library.


We hit directly the ber_bvecfree() without accessing this page and…


… Crash :)


Also, apparently the PoC shows just a crash caused by overwriting some registers, which results in an access violation. It looks like none of these registers can be used to achieve code execution.




It could still be that choosing carefully our buffer we could avoid the crash at ber_bvecfree() and somehow reach a dangerous portion of the code, but this is a looong shot…



Some useful links (IBM) (PoC available :)) (ZDI) <— Detailed explanation!


5 thoughts on “CVE-2011-1206. IBM Tivoli LDAP

  1. holy shit!
    n1 tutorial.

    womit hast du diese netten grafiken gemacht (die orangen kästchen?).
    Und noch viel interessanter: Woher hast du den IBM-Tivoli-Server? Oo

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s