The Oracle Network Architecture

Overview

The Oracle network architecture encompasses many components - all of which neatly corresponds to the OSI networking model (see Figure 2-1). This architecture enables Oracle client and server applications to transparently communicate over protocols such as TCP/IP. The session protocol that interfaces between the applications (Oracle Call Interface, or OCI, on the client and Oracle Program Interface, or OPI, on the server) and the network layer is known as Net8 (Net9), and before that SQL*Net. Between the OCI/OPI and Net8 layer is a presentation protocol called Two-Task Common (TTC) that is responsible for character set and data type conversion differences between the client and the server. The Net8 session protocol has three components - the Net Foundation and Routing/Naming/Auth and TNS - the last two making up Protocol Support. Supported transport protocols include TCP/IP, with or without TCP, Named Pipes and Sockets Direct Protocol (SDP), which enables communication over Infiband high-speed networks. Underpinning all of this is the Transparent Network Substrate protocol, also known as TNS. The task of TNS is to select the Oracle Protocol Adapter, wrapping the communication in one of the supported transport protocols.

image from book
Figure 2-1: The OSI networking model


The TNS Protocol

When developing raw tools to troubleshoot problems in Oracle, it is necessary to understand the TNS protocol. This section details information about the TNS protocol. The Oracle JDBC client (classes12.zip) is a useful resource when seeking to understand the TNS protocol.

The TNS Header

Every TNS packet has an eight-byte header. The first two bytes (WORD) of the header are used for the packet length - inclusive of the header size. The size, like all values, is big-endian. The next WORD is for the packet checksum if checksumming is done - by default it is not, and the value for this WORD is 0x0000. The next byte is used to indicate the packet type - for example, the most common are as follows:

Connect packet Type 1
Accept packet Type 2

Ack packet Type 3
Refuse packet Type 4
Redirect packet Type 5
Data packet Type 6
NULL packet Type 7
Abort packet Type 9
Resend packet Type 11
Marker packet Type 12
Attention packet Type 13
Control packet Type 14

When connecting to Oracle, at the TNS level the client sends the server a Connect packet (type 1) specifying the service name they wish to access. Provided the Listener knows of such a service, one of two things can happen: The Listener could send an Accept packet (type 2) or it could redirect the client to another port with a Redirect packet (type 5). If the former option occurs, then the client attempts to authenticate. This is covered in detail in Chapter 4, "Attacking the Authentication Process." If the latter occurs, then the client sends a Connect packet to the port to which they've been redirected and requests access to the service. If all goes well, the server issues an Accept packet and authentication takes place. All authentication packets are Data packets with a type of 6.

Going back, if the Listener does not know of the service to which the client is requesting access, then it issues a Refuse packet - type 4. Once authenticated, queries and results packets are Data packets. Every so often you'll see a packet of type 12 (0x0C) - this is a Marker packet, which is used for interrupting. For example, if the server wishes the client to stop sending data, then it will send the client a Marker packet.

Continuing with the details of the TNS header, the next byte is the header flags. Generally the flags are unused, but the 10g client may set the value to 0x04.

The final two bytes form a WORD for the header checksum - not used by default and set to 0x0000:

WORD 00 00 Packet Size
WORD 00 00 Packet Checksum
BYTE 00 Packet Type
BYTE 00 Flags
WORD 00 00 Header Checksum

Before delving further into the packet, it would be useful to take a look at Refuse packets - type 4. Refuse packets indicate an error of some kind - for example, a logon denied error with an "invalid username/password" - ORA-01017. With these errors, the 54th byte indicates the problem. A 3 is an invalid password; a 2 indicates no such user. Clearly, you can derive potentially useful information even from Refuse packets.

Inside the Packet

Most packets you'll see on the wire are Data packets (type 6). With Data packets, the WORD after the TNS header is for the Data Flags. If the packet is a disconnect packet, then this WORD is set to 0x0040 - otherwise, it is generally 0x0000.

  Note 

There is a bug in all versions of Oracle when a server processes a Data packet (type 6) that has the second bit of the Data Flags set but the first (least significant) bit unset (e.g., numbers 2, 6, 10, 14, and so on). When the server receives such a packet it winds up in an endless loop, hogging all available CPU processing time. Obviously, this negatively impacts server performance.

The next byte after the Data Flags (byte 11) determines what's in the Data packet:

  • 0x01 indicates protocol negotiation. Here, the client sends to the server acceptable protocol versions - these are 6, 5, 4, 3, 2, 1, and 0. The server will reply with a common version - for example, 6 or 5 - but it will also send over information such as the character set it uses, how many characters are in the set, a version string, and server flags.
  • 0x02 indicates an exchange of data type representations.
  • 0x03 indicates a Two-Task Interface (TTI) function call. The following table lists some of the functions:

     0x02 Open
     0x03 Query
     0x04 Execute
     0x05 Fetch
     0x08 Close
     0x09 Disconnect/logoff
     0x0C AutoCommit ON
     0x0D AutoCommit OFF
     0x0E Commit
     0x0F Rollback
     0x14 Cancel
     0x2B Describe
     0x30 Startup
     0x31 Shutdown
    0x3B Version
    0x43 K2 Transactions
    0x47 Query
    
    0x4A OSQL7
    0x5C OKOD
    0x5E Query
    0x60 LOB Operations
    0x62 ODNY
    0x67 Transaction - end
    0x68 Transaction - begin
    0x69 OCCA
    0x6D Startup
    0x51 Logon (present password)
    0x52 Logon (present username)
    0x73 Logon (present password - send AUTH_PASSWORD)
    0x76 Logon (present username - request AUTH_SESSKEY)
    0x77 Describe
    0x7F OOTCM
    0x8B OKPFC
    

Some of these may be called prior to authentication - for example, the Version (0x3B) TTI function:

  • 0x08 indicates "OK" - sent from the server in response to a client.
  • 0x11 indicates extended TTI functions. These were introduced in later versions of Oracle, so for backward compatibility do not use 0x03.
  • Here are some function codes:

    0x6b Switch or Detach session
    0x78 Close
    0x87 OSCID
    0x9A OKEYVAL
    
  • 0x20 is used when calling external procedures and with service registrations.
  • 0x44 is also used when calling external procedures and with service registrations.

The best way to get a handle on the TNS protocol, other than examining the Oracle JDBC client (classes12.zip), is to grab some packets off the wire with a network sniffer and see what's going on.

Throughout the remainder of this book you'll see a number of packet dumps; refer back to this chapter when examining the contents.


Getting the Oracle Version

There are many ways of getting the Oracle version number back prior to authentication - so many in fact that people who worry about attackers being able to get the version number should stop worrying and just accept it. Of course, you can use TCP node valid checking and set firewall rules to disallow traffic, but if an attacker is coming from an "allowed" source, i.e., node, then you'll have to put up with people being able to get your version number; doing so is built into the protocol itself. Let's explore a few of the ways to get the version number - some methods are widely known, others less so.

The Listener Version and Status Command

The Listener Control Utility has both a version command and a status command. They both can be issued from the client to request the Listener's version number. Details about the operating system on which the Listener runs are also revealed. Note that while Oracle prevents the status command from working on 10g remotely, version still works. Here's the output from a version command:

C:>lsnrctl

LSNRCTL for 32-bit Windows: Version 8.1.7.4.0 - Production on 19-JUN-2006
17:54:42

(c) Copyright 1998 Oracle Corporation. All rights reserved.

Welcome to LSNRCTL, type "help" for information.

LSNRCTL> set current_listener 192.168.0.120
Current Listener is 192.168.0.120
LSNRCTL> version
Connecting to
(DESCRIPTION=(CONNECT_DATA=(SID=*)(SERVICE_NAME=192.168.0.120))(ADDRESS=
(PROTOCOL=TCP)(HOST=192.168.0.120)(PORT=1521)))
TNSLSNR for 32-bit Windows: Version 10.2.0.1.0 - Production
 TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
 Windows NT Named Pipes NT Protocol Adapter for 32-bit Windows:
Version 10.2.0.1.0 - Production
 Windows NT TCP/IP NT Protocol Adapter for 32-bit Windows:
Version 10.2.0.1.0 - Production,,
The command completed successfully
LSNRCTL>

From the preceding output, you can see that the server is running 10g Release 2 on Windows.

Using the TNS Protocol Version

In a TNS connect packet, the WORD (which is two bytes in size) found nine bytes into the packet specifies the TNS protocol version being used. The next WORD, bytes 11 and 12, specifies the earliest version number the sending system understands. For example, if an Oracle client running version 8.1.7.4 connects to the Listener on an Oracle server, then the client sends 0x0136 as the TNS protocol version being used and 0x012C as the earliest version it understands. This way, two different versions of Oracle can communicate by choosing a TNS version they both understand. Clearly, because later versions of Oracle use improved versions of the TNS protocol, we can use these two WORDs to determine the server version. We do this by specifying the earliest version number as the same version as the current version being used - so both WORDs in the connect packet have the same value. Furthermore, we start with a high version number, one known not to exist yet, and work down. Thus, we send 0x13C for both the current and earliest versions in our first connect packet. The server should not understand this version so it will either time-out or produce an error. Next, we reduce this to 0x13B, then to 0x13A, then to 0x139, and so on all the way down to 0x00CC. As soon as the server starts responding as expected, we know the latest version of the TNS protocol it speaks. From this we can deduce what the Oracle greater version is, e.g., 10, 9, or 8, etc.

Oracle 10r2 supports 0x139
Oracle 9r2 supports 0x138
Oracle 9i supports 0x137
Oracle 8 supports 0x136.

For more information, see tnsver.c at the end of this chapter.

Using the XML Database Version

If the XML database is running, one can telnet to TCP port 2100 to get the version information. This service is an ftp service and the banner reveals the information:

220 PILUM FTP Server (Oracle XML DB/Oracle9i Enterprise Edition Release
9.2.0.1.0 - Production) ready.

Also, the XDB Web server on TCP port 8080 gives up the version number:

GET / HTTP/1.1
Host: PILUM

HTTP/1.1 401 Unauthorized
MS-Author-Via: DAV
DAV: 1,2,<">http://www.oracle.com/xdb/webdav/props>
Server: Oracle XML DB/Oracle9i Enterprise Edition Release 9.2.0.1.0 -
Production

WWW-Authenticate: Basic Realm=" XDB"
Date: Mon, 19 Jun 2006 18:57:59 GMT
Content-Type: text/html
Content-Length: 147

Using TNS Error Text

If the Listener receives a TNS command it doesn't understand, it sends an error back. This error text contains a VSNNUM that holds a decimal number such as 169869568. If we convert this number to hexadecimal, look what we get: 0x0A200100. This is the Oracle version number in disguise - in this case 10.2.0.1.0. The following packer dump is taken from a Listener that doesn't understand the ‘unbreakable' command:

IP Header
 Length and version: 0x45
 Type of service: 0x00
 Total length: 181
 Identifier: 13914
 Flags: 0x4000
 TTL: 128
 Protocol: 6 (TCP)
 Checksum: 0x41e5
 Source IP: 192.168.0.120
 Dest IP: 192.168.0.59
TCP Header
 Source port: 1521
 Dest port: 3004
 Sequence: 1152664576
 ack: 2478634793
 Header length: 0x50
 Flags: 0x18 (ACK PSH)
 Window Size: 17451
 Checksum: 0xcae1
 Urgent Pointer: 0
Raw Data
 00 8d 00 00 04 00 00 00 22 00 00 81 28 44 45 53 " (DES
 43 52 49 50 54 49 4f 4e 3d 28 45 52 52 3d 31 31 CRIPTION=(ERR=11
 35 33 29 28 56 53 4e 4e 55 4d 3d 31 36 39 38 36 53)(VSNNUM=16986
 39 35 36 38 29 28 45 52 52 4f 52 5f 53 54 41 43 9568)(ERROR_STAC
 4b 3d 28 45 52 52 4f 52 3d 28 43 4f 44 45 3d 31 K=(ERROR=(CODE=1
 31 35 33 29 28 45 4d 46 49 3d 34 29 28 41 52 47 153)(EMFI=4)(ARG
 53 3d 27 75 6e 62 72 65 61 6b 61 62 6c 65 27 29 S='unbreakable')
 29 28 45 52 52 4f 52 3d 28 43 4f 44 45 3d 33 30 )(ERROR=(CODE=30
 33 29 28 45 4d 46 49 3d 31 29 29 29 29 3)(EMFI=1))))

See tnsver.c at the end of this chapter.

Using the TNS Version TTC Function

We discussed TTC functions earlier and mentioned the version function 0x3B. This will cause an Oracle server to reveal its version prior to authentication.

Using Additional Network Option Negotiation

Once an Accept packet has been received by the client from the server, the client may choose to negotiate additional network services such as Authentication, Encryption, Data Integrity, and Supervisor. The version of the client or server can be found three bytes after the ANO negotiation header (0xDEADBEEF) - 17 bytes into the packet. In the following capture, you can see the version is 8.1.7.4 - highlighted in bold:

IP Header
 Length and version: 0x45
 Type of service: 0x00
 Total length: 203
 Identifier: 14473
 Flags: 0x4000
 TTL: 128
 Protocol: 6 (TCP)
 Checksum: 0x3fa0
 Source IP: 192.168.0.59
 Dest IP: 192.168.0.120
TCP Header
 Source port: 4194
 Dest port: 1495
 Sequence: 422372252
 ack: 597087647
 Header length: 0x50
 Flags: 0x18 (ACK PSH)
 Window Size: 65087
 Checksum: 0x7e36
 Urgent Pointer: 0
Raw Data
 00 a3 00 00 06 00 00 00 00 00 de ad be ef 00 99
 08 10 74 00 00 04 00 00 04 00 03 00 00 00 00 00 t
 04 00 05 08 10 74 00 00 02 00 06 00 1f 00 0e 00 t
 01 de ad be ef 00 03 00 00 00 02 00 04 00 01 00
 01 00 07 00 00 00 00 00 04 00 05 08 10 74 00 00 t
 02 00 06 fa ff 00 01 00 02 01 00 03 00 00 4e 54 NT
 53 00 04 00 05 02 00 00 00 00 04 00 04 00 00 00 S
 00 00 04 00 04 00 00 00 02 00 02 00 02 00 00 00
 00 00 04 00 05 08 10 74 00 00 01 00 02 00 00 03 t
 00 02 00 00 00 00 00 04 00 05 08 10 74 00 00 01 t
 00 02 00


Wrapping Up

This chapter described the TNS protocol and some of the ways in which it is possible to get the version number using features in the protocol. You can see how this occurs from tnsver.c:

/************************************
/ Compile from a command line
/
/ C:>cl /TC tnsver.c /link wsock32.lib
/
*/

#include 
#include 
#include 


struct hostent *he;
struct sockaddr_in s_sa;
int ListenerPort=1521;
char host[260]="";

int GetOracleVersion(unsigned char *pkt, unsigned int pkt_len, unsigned
char *resp, unsigned int resp_len, int dr);
int StartWinsock(void);
int InitTNSPacket(unsigned char *data, unsigned short data_length);
int bswap_s(unsigned int v);
int bswap_i(unsigned int v);
int error();
int GetOracleVersionByError();
int GetOracleVersionByProtocolVersion();
int GetOracleVersionByVersionCommand();


typedef struct TNSHeader {
 unsigned short Length;
 unsigned short PacketChecksum;
 unsigned char Type;
 unsigned char Flags;
 unsigned short HeaderChecksum;
 }TNSHeader;

typedef struct TNSConnect {
 unsigned short Version;
 unsigned short MinVersion;
 unsigned short GlobalServiceOptions;
 unsigned short SessionDataUnit;
 unsigned short TransportDataUnit;

 unsigned short Characteristics;
 unsigned short MaxPacketsBeforeAck;
 unsigned short ByteOrder;
 unsigned short Length;
 unsigned short Offset;
 unsigned int MaxRecv;
 unsigned short AdditionalNetworkOptions;
 unsigned char buf[24];
} TNSConnect;

#define TNS_CONNECT 1
#define MAX_VER 0x0139
#define MIN_VER 0x012C
#define SDU_MAX 0x0800
#define TDU_MAX 0x7FFF
#define DATA_LENGTH 12



unsigned char TNSPacket[2000]="";

int InitTNSPacket(unsigned char *data, unsigned short data_length)
{
 TNSConnect tnsconnect;
 TNSHeader tnsheader;

 memset(&tnsheader,0,sizeof(TNSHeader));
 memset(&tnsconnect,0,sizeof(TNSConnect));

 tnsheader.Length = bswap_s(data_length + 0x3A);
 tnsheader.PacketChecksum = 0;
 tnsheader.Type = TNS_CONNECT;
 tnsheader.Flags = 0;
 tnsheader.HeaderChecksum = 0;

 tnsconnect.Version = bswap_s(MAX_VER);
 tnsconnect.MinVersion = bswap_s(MIN_VER);
 tnsconnect.GlobalServiceOptions = 0;
 tnsconnect.SessionDataUnit = bswap_s(SDU_MAX);
 tnsconnect.TransportDataUnit = bswap_s(TDU_MAX);
 tnsconnect.Characteristics = bswap_s(0x860E);
 tnsconnect.MaxPacketsBeforeAck = 0;
 tnsconnect.ByteOrder = 0x1;
 tnsconnect.Length = bswap_s(data_length);
 tnsconnect.Offset = bswap_s(0x3A);
 tnsconnect.MaxRecv = bswap_i(0x000007F8);
 tnsconnect.AdditionalNetworkOptions = 0x0C0C;

 memmove(TNSPacket,&tnsheader,sizeof(TNSHeader));
 memmove(&TNSPacket[sizeof(TNSHeader)],&tnsconnect,50);

 memmove(&TNSPacket[0x3A],data,data_length);

 return 0;

}



int main(int argc, char *argv[])
{
 unsigned int err=0;
 unsigned short val = 0x13B;


 if(argc == 1)
 {
 printf("
	*** OraVer ***");
 printf("

	Gets the Oracle version number.");
 printf("

	C:\>%s host [port]",argv[0]);
 printf("

	David
Litchfield
	davidl@ngssoftware.com
	22th April 2003
");
 return 0;
 }

 strncpy(host,argv[1],256);

 if(argc == 3)
 ListenerPort = atoi(argv[2]);

 err = StartWinsock();
 if(err==0)
 printf("Error starting Winsock.
");
 else
 {

 GetOracleVersionByError();
 GetOracleVersionByProtocolVersion();
 GetOracleVersionByVersionCommand();
 }

 WSACleanup();
 return 0;

}

int GetOracleVersionByProtocolVersion()
{
 int res=0;
 unsigned char buff[2000]="";
 unsigned char *ptr = NULL;
 unsigned short ver = 0x13B;

 InitTNSPacket("AAAABBBBCCCC",DATA_LENGTH);

 while(ver > 0xCC)
 {
 ver = (unsigned short)bswap_s(ver);
 memmove(&TNSPacket[8],&ver,2);
 memmove(&TNSPacket[10],&ver,2);
 ver = (unsigned short)bswap_s(ver);
 res =
GetOracleVersion(TNSPacket,0x3A+DATA_LENGTH,buff,2000,0);
 if(res == -1)
 return printf("Failed to connect.
");
 if(res > 0x20)
 {
 printf("TNS version 0x%.2X is supported
",ver);
 break;
 }
 else
 printf("TNS version 0x%.2X is not supported
",ver);
 ver --;
 }
 return 0;
}


int GetOracleVersionByVersionCommand()
{
 int res=0;
 unsigned char buff[2000]="";
 unsigned char *ptr = NULL;
 unsigned char *vercmd = "(CONNECT_DATA=(COMMAND=version))";

 InitTNSPacket(vercmd,(unsigned short)strlen(vercmd));

 res = GetOracleVersion(TNSPacket,0x3A+strlen(vercmd),buff,2000,1);
 if(res == -1)
 return printf("Failed to connect.
");
 if(res > 0x36)
 {
 ptr = &buff[10];
 printf("

Version command:
%s
",ptr);

 }
 else
 error();

 return 0;
}

int GetOracleVersionByError()
{

 int res=0;
 unsigned char buff[2000]="";
 unsigned char ver[8]="";
 unsigned char *ptr = NULL;
 unsigned char h=0,l=0,p=0,q=0;

 InitTNSPacket("ABCDEFGHIJKL",DATA_LENGTH);
 res = GetOracleVersion(TNSPacket,0x3A+DATA_LENGTH,buff,2000,0);
 if(res == -1)
 return printf("Failed to connect to listener.
");
 if(res > 0x32)
 {
 ptr = &buff[36];
 ptr[6]=0x00;
 if(strcmp(ptr," VSNNUM")==0)
 {
 ptr = &ptr[7];
 res = atoi(ptr);
 res = res << 4;
 memmove(ver,&res,4);
 h = ver[3] >> 4;
 l = ver[3] << 4;
 l = l >> 4;
 p = ver[1] >> 4;
 q = ver[0] >> 4;
 printf("
Version of Oracle is
%d.%d.%d.%d.%d

",h,l,ver[2],p,q);
 }
 else
 return error();
 }
 else
 return error();
 return 0;
}

int error()
{
 return printf("There was an error getting the version
number.
");
}

int bswap_s(unsigned int v)
{
 __asm {
 xor eax, eax
 mov eax,v
 bswap eax
 shr eax,16
 mov v, eax
 }

 return v;
}

int bswap_i(unsigned int v)
{
 __asm {
 xor eax, eax
 mov eax,v
 bswap eax
 mov v, eax
 }
 return v;
}

int StartWinsock()
{
 int err=0;
 unsigned int addr;
 WORD wVersionRequested;
 WSADATA wsaData;

 wVersionRequested = MAKEWORD(2, 0);
 err = WSAStartup(wVersionRequested, &wsaData);
 if (err != 0)
 return 0;

 if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion)
!= 0)

 return 0;
 s_sa.sin_addr.s_addr=INADDR_ANY;
 s_sa.sin_family=AF_INET;

 if (isalpha(host[0]))
 {
 he = gethostbyname(host);
 if(he == NULL)
 {
 printf("Failed to look up %s
",host);
 return 0;
 }
 memcpy(&s_sa.sin_addr,he->h_addr,he->h_length);
 }
 else
 {
 addr = inet_addr(host);
 memcpy(&s_sa.sin_addr,&addr,4);
 }

 return 1;
}



int GetOracleVersion(unsigned char *pkt, unsigned int pkt_len, unsigned
char *resp, unsigned int resp_len, int dr)
{

 unsigned char ver[8]="";
 unsigned char h=0,l=0,p=0,q=0;
 int snd=0,rcv=0,count=0;
 unsigned int to=1000;
 SOCKET cli_sock;
 char *ptr = NULL;

 cli_sock=socket(AF_INET,SOCK_STREAM,0);
 if (cli_sock==INVALID_SOCKET)
 return printf("
Failed to create the socket.
");

 setsockopt(cli_sock,SOL_SOCKET,SO_RCVTIMEO,(char
*)&to,sizeof(unsigned int));

 s_sa.sin_port=htons((unsigned short)ListenerPort);
 if
(connect(cli_sock,(LPSOCKADDR)&s_sa,sizeof(s_sa))==SOCKET_ERROR)
 {
 printf("
Failed to connect to the Listener.
");
 return -1;
 }

 snd=send(cli_sock, pkt , pkt_len , 0);


 rcv = recv(cli_sock,resp,resp_len,0);
 if(dr)
 rcv = recv(cli_sock,resp,resp_len,0);
 closesocket(cli_sock);

 if(rcv == SOCKET_ERROR)
 return 0;
 else
 return rcv;
}

Later in the book, you'll learn how to get the version information down to the exact patch level.




Oracle Hacker's Handbook. Hacking and Defending Oracle
The Oracle Hackers Handbook: Hacking and Defending Oracle
ISBN: 0470080221
EAN: 2147483647
Year: 2004
Pages: 101

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net