Call: +44 (0)7759 277220 Call
PeteFinnigan.com Limited Products, Services, Training and Information
Blog

Pete Finnigan's Oracle Security Weblog

This is the weblog for Pete Finnigan. Pete works in the area of Oracle security and he specialises in auditing Oracle databases for security issues. This weblog is aimed squarely at those interested in the security of their Oracle databases.

Forensic Analysis for records in Oracle with no Timestamp

If we have an Oracle database table with no timestamp can we still get a date/time stamp when something happened?

For instance if a table has a date/time column then yes of course we can know when the records changed assuming the standard "last updated by" and "last updated when" columns like we see in applications such as EBS.

If we have auditing enabled on the table we are interested in and it captures the right events then yes, we can see when a record changed or was added or deleted in that table.

If the database is in archivelog mode and the archive logs are available then we can use the redo/archive logs and log miner to retrieve changes from the database that affect the table we are interested in.

BUT, inevitably there will be no column, audit or redo available so is there any other way we can find out what happened?

A good example would be system grants in the database. So for instance

SQL> grant create session to sec_auditor;

So, we can see grants in the DBA_SYS_PRIVS view and this has the following structure:

C:\>sqlplus sys/oracle@//192.168.56.34:1521/freepdb1 as sysdba

SQL*Plus: Release 19.0.0.0.0 - Production on Sun Jun 7 09:28:16 2026
Version 19.28.0.0.0

Copyright (c) 1982, 2025, Oracle. All rights reserved.


Connected to:
Oracle AI Database 26ai Free Release 23.26.0.0.0 - Develop, Learn, and Run for Free
Version 23.26.0.0.0

SQL> desc dba_sys_privs
Name Null? Type
----------------------------------------- -------- ----------------------------
GRANTEE VARCHAR2(128)
PRIVILEGE VARCHAR2(40)
ADMIN_OPTION VARCHAR2(3)
COMMON VARCHAR2(3)
INHERITED VARCHAR2(3)

SQL>

As we can see there is no date/timestamp for system privilege grants. I would like to be able to map them if possible as part of an Oracle database security audit opr as part of a forensic investigation of the database.

What is the base tables:

SQL> alter session set container=cdb$root;

Session altered.

SQL>

SQL> set long 1000000
SQL> select text from dba_views where view_name='DBA_SYS_PRIVS';

TEXT
--------------------------------------------------------------------------------
select u.name,spm.name,decode(min(mod(option$, 2)),1,'YES','NO'),
'NO', 'NO'
from sys.system_privilege_map spm, sys.sysauth$ sa, user$ u
where sa.grantee#=u.user# and sa.privilege#=spm.privilege
and bitand(nvl(option$, 0), 4) = 0
group by u.name,spm.name
union all
/* Commonly granted Privileges */
select u.name,spm.name,decode(min(bitand(option$, 16)),16,'YES','NO'),
'YES', decode(SYS_CONTEXT('USERENV', 'CON_ID'), 1, 'NO', 'YES')
from sys.system_privilege_map spm, sys.sysauth$ sa, user$ u

TEXT
--------------------------------------------------------------------------------
where sa.grantee#=u.user# and sa.privilege#=spm.privilege
and bitand(option$,8) = 8
group by u.name,spm.name
union all
/* Federationally granted Privileges */
select u.name,spm.name,decode(min(bitand(option$, 128)),128,'YES','NO'),
'YES',
decode(SYS_CONTEXT('USERENV', 'IS_APPLICATION_PDB'), 'YES', 'YES', 'NO')
from sys.system_privilege_map spm, sys.sysauth$ sa, user$ u
where sa.grantee#=u.user# and sa.privilege#=spm.privilege
and bitand(option$,64) = 64

TEXT
--------------------------------------------------------------------------------
group by u.name,spm.name


SQL>

Quite a complex union of 3 SQLs mapping the user names for grantee and also linking to the system_privilege_map and mainly sys.sysauth$. All the privilege records are basically stored in sysauth$. What is the structure of this table:

SQL> desc sysauth$
Name Null? Type
----------------------------------------- -------- ----------------------------
GRANTEE# NOT NULL NUMBER
PRIVILEGE# NOT NULL NUMBER
SEQUENCE# NOT NULL NUMBER
OPTION$ NUMBER

SQL>

No timestamp is available on this table so we do not have any native columns. The Oracle database uses SCNs (System Change Number) and whilst these are not a timestamp they are useful as they order records. There is a table in the database that is managed by SMON_SCN_TIME table that maintains a short window of mappings between SCN numbers and timestamps. So if the SCN is recent then if we have an SCN we can map to a timestamp. Sounds like it could be useful but there is a window of accuracy with the mapping of 3 seconds so the timestamp can be out by 3 seconds.

So, can we get the SCN from the SYSAUTH$ table for all records / rows?

SQL> select ora_rowscn,grantee#,privilege#,sequence#,option$ from sysauth$ order by sequence#;

ORA_ROWSCN GRANTEE# PRIVILEGE# SEQUENCE# OPTION$
---------- ---------- ---------- ---------- ----------
501880 0 -352 1 12
501880 0 2 2 28
501880 2 -5 3 12
501880 0 3 4 28
501880 3 -40 5 12
501880 3 -60 6 12
501880 3 -80 7 12
501880 3 -90 8 12
501880 3 -105 9 12
501880 3 -140 10 12
501880 3 -151 11 12

ORA_ROWSCN GRANTEE# PRIVILEGE# SEQUENCE# OPTION$
---------- ---------- ---------- ---------- ----------
501880 3 -172 12 12
501880 3 -180 13 12
...
4605568 237 -265 3826
4605568 237 -80 3827
4605568 237 -44 3828
4605568 237 -71 3829
4605568 237 -73 3830
4605568 241 -165 3835
4605568 243 241 3836
4605568 244 242 3837
4605568 243 240 3838
4605568 244 240 3839
4605568 237 239 3840

ORA_ROWSCN GRANTEE# PRIVILEGE# SEQUENCE# OPTION$
---------- ---------- ---------- ---------- ----------
4605568 237 240 3841

1332 rows selected.

SQL>

A few interesting points pop up. 1) There are 1332 rows in this table so you would assume currently there are 1332 system privilege grants:

SQL> select count(*) from dba_sys_privs;

COUNT(*)
----------
1089

SQL>

No, 1089 grants not 1332. We could analyse the difference but that is moving off topic for this discussion. 2) the last row sequence# is 3841 so the assumption here is that there are a very large number of rows missing if we assume one sequence per row and its incremented every time a row is added. Also a quick look shows that there are a lot of gaps in the sequence numbers so that supports the theory of deletions or more correctly revokes of grants.

The biggest issue for our investigation is that the SCN is stored at the block level not the row level. We can see from the output that there are few actual SCNs per row:

SQL> select count(*),ora_rowscn from sysauth$ group by ora_rowscn;

COUNT(*) ORA_ROWSCN
---------- ----------
379 501880
382 617063
368 995672
203 4605568

SQL>

As we can see this table currently uses 4 blocks in my database and from the numbers assigned to each SCN we can see the relative similar number of records for the full blocks as 368 - 382 and clearly the last block with just 203 records is not full yet. We can also see the block numbers in this SQL:

SQL> select count(*),dbms_rowid.rowid_relative_fno(rowid) AS file_no,dbms_rowid.rowid_block_number(rowid) AS block_no from sysauth$ group by dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid);

COUNT(*) FILE_NO BLOCK_NO
---------- ---------- ----------
382 0 1490
379 0 1489
368 0 1491
203 0 1492

SQL>

This proves the SCN is per block and each block has between 203 (latest and filling up) and 382 rows

Lets see the timestamp per block

SQL> select distinct scn_to_timestamp(ora_rowscn) from sysauth$;
select distinct scn_to_timestamp(ora_rowscn) from sysauth$
*
ERROR at line 1:
ORA-08181: specified number is not a valid system change number
ORA-06512: at "SYS.SCN_TO_TIMESTAMP", line 1


SQL>

The SCN to timestamp mapping is stated to have a precision of 3 seconds, and up to 120 hours of mapping if automatic undo management is used.- SCN to Timestamp Mapping.

The ORA-08181 message means that the mapping is not stored anymore as the database holds limited mapping

The mapping is in the table sys.smon_scn_time

SQL> desc smon_scn_time
Name Null? Type
----------------------------------------- -------- ----------------------------
THREAD NUMBER
TIME_MP NUMBER
TIME_DP DATE
SCN_WRP NUMBER
SCN_BAS NUMBER
NUM_MAPPINGS NUMBER
TIM_SCN_MAP RAW(1200)
SCN NUMBER
ORIG_THREAD NUMBER

SQL>

We can check the max time range in the table with:

SQL> select to_char(min(time_dp),'DD-MON-YYYY HH24:MI:SS'),to_char(max(time_dp),'DD-MON-YYYY HH24:MI:SS') from smon_scn_time;

TO_CHAR(MIN(TIME_DP),'DD-MON- TO_CHAR(MAX(TIME_DP),'DD-MON-
----------------------------- -----------------------------
20-OCT-2025 15:18:02 07-JUN-2026 08:54:39

SQL>

The range in my database is not realistic as this is a 26ai OVA and I installed it two weeks ago so the date range is in reality smaller

So, can we at least get a time for each block

SQL> select distinct scn_to_timestamp(ora_rowscn) tim,dbms_rowid.rowid_block_number(rowid) rod from sysauth$ where dbms_rowid.rowid_block_number(rowid)=1492;

TIM ROD
---------------------------------------- -----
05-JUN-26 02.44.52.000000000 PM 1492

SQL>

The other 3 older blocks give ORA-08181 error that the SCN to time map is too old.

SQL> select distinct scn_to_timestamp(ora_rowscn) tim,dbms_rowid.rowid_block_number(rowid) rod from sysauth$ where dbms_rowid.rowid_block_number(rowid)=1491;
select distinct scn_to_timestamp(ora_rowscn) tim,dbms_rowid.rowid_block_number(rowid) rod from sysauth$ where dbms_rowid.rowid_block_number(rowid)=1491
*
ERROR at line 1:
ORA-08181: specified number is not a valid system change number
ORA-06512: at "SYS.SCN_TO_TIMESTAMP", line 1


SQL> select distinct scn_to_timestamp(ora_rowscn) tim,dbms_rowid.rowid_block_number(rowid) rod from sysauth$ where dbms_rowid.rowid_block_number(rowid)=1490;
select distinct scn_to_timestamp(ora_rowscn) tim,dbms_rowid.rowid_block_number(rowid) rod from sysauth$ where dbms_rowid.rowid_block_number(rowid)=1490
*
ERROR at line 1:
ORA-08181: specified number is not a valid system change number
ORA-06512: at "SYS.SCN_TO_TIMESTAMP", line 1


SQL> select distinct scn_to_timestamp(ora_rowscn) tim,dbms_rowid.rowid_block_number(rowid) rod from sysauth$ where dbms_rowid.rowid_block_number(rowid)=1489;
select distinct scn_to_timestamp(ora_rowscn) tim,dbms_rowid.rowid_block_number(rowid) rod from sysauth$ where dbms_rowid.rowid_block_number(rowid)=1489
*
ERROR at line 1:
ORA-08181: specified number is not a valid system change number
ORA-06512: at "SYS.SCN_TO_TIMESTAMP", line 1


SQL>

We can also get the min and max timestamps and SCNs and also work out on average how many SCNs per second:

SQL> select to_char(min(time_dp),'DD-MON-YYYY HH24:MI:SS'),to_char(max(time_dp),'DD-MON-YYYY HH24:MI:SS'),min(scn),max(scn),max(scn)-min(scn) diff,(max(time_dp)-min(time_dp))*86400 sec, ((max(time_dp)-min(time_dp))*86400)/(max(scn)-min(scn)) from smon_scn_time where to_char(time_dp,'MM-YYYY')='06-2026';

TO_CHAR(MIN(TIME_DP),'DD-MON- TO_CHAR(MAX(TIME_DP),'DD-MON- MIN(SCN)
----------------------------- ----------------------------- ----------
MAX(SCN) DIFF SEC
---------- ---------- ----------
((MAX(TIME_DP)-MIN(TIME_DP))*86400)/(MAX(SCN)-MIN(SCN))
-------------------------------------------------------
01-JUN-2026 06:12:37 07-JUN-2026 09:24:41 4390900
4755799 364899 529924
1.45224843


SQL>

So 1.45 SCN per second are generated.

The previous blocks are not in the same scn range but we could calculate fir fun

1 - 4390900 - 995672 = 3395228 SCN / 1.45 = 2,341,536 seconds =

SQL> SELECT to_char(TO_DATE('01-JUN-2026 06:12:37','DD-MON-YYYY HH24:MI:SS')-(2341536/86400),'DD-MON-YYYY HH24:MI:SS') from dual;

TO_CHAR(TO_DATE('01-
--------------------
05-MAY-2026 03:47:01

SQL>

2 - 4390900 - 617063 = 3,773,837/1.45 = 2,602,646.2 =

SQL> SELECT to_char(TO_DATE('01-JUN-2026 06:12:37','DD-MON-YYYY HH24:MI:SS')-(2602646/86400),'DD-MON-YYYY HH24:MI:SS') from dual;

TO_CHAR(TO_DATE('01-
--------------------
02-MAY-2026 03:15:11

SQL>

3 - 4390900 - 501880 = 3889020/1.45=2682082 =

SQL> SELECT to_char(TO_DATE('01-JUN-2026 06:12:37','DD-MON-YYYY HH24:MI:SS')-(2682082/86400),'DD-MON-YYYY HH24:MI:SS') from dual;

TO_CHAR(TO_DATE('01-
--------------------
01-MAY-2026 05:11:15

SQL>

This is clearly wrong as the min time in the SCN map was October 2025 BUT this is a pre-built 26ai OVA and it was not run until 01-JUN-2026 by me. BUT the calculations show how you may get a very rough ball park for a system that is not an OVA that has been stopped and not used until someone starts to install and use it

We can get the last change at a block level - i.e. a date time for the last grant of a SYSTEM privilege but we cannot get timestamps for each grant done since I started to use the database unless we have an audit trail

BUT, often when an attack occurs we want to try and understand what happened and when and part of that is system grants

We can also repeat the calculation not from current SCN map but also for the older blocks with the last SCN map:


SQL> SELECT to_char(TO_DATE('20-OCT-2025 15:18:02','DD-MON-YYYY HH24:MI:SS')-(2341536/86400),'DD-MON-YYYY HH24:MI:SS') from dual;

TO_CHAR(TO_DATE('20-
--------------------
23-SEP-2025 12:52:26

SQL> SELECT to_char(TO_DATE('20-OCT-2025 15:18:02','DD-MON-YYYY HH24:MI:SS')-(2602646/86400),'DD-MON-YYYY HH24:MI:SS') from dual;

TO_CHAR(TO_DATE('20-
--------------------
20-SEP-2025 12:20:36

SQL> SELECT to_char(TO_DATE('20-OCT-2025 15:18:02','DD-MON-YYYY HH24:MI:SS')-(2682082/86400),'DD-MON-YYYY HH24:MI:SS') from dual;

TO_CHAR(TO_DATE('20-
--------------------
19-SEP-2025 14:16:40

SQL>

This is probably more realistic

What is database created date?

SQL> select to_char(created,'DD-MON-YYYY HH24:MI:SS') from v$database;

TO_CHAR(CREATED,'DD-MON-YYYYH
-----------------------------
20-OCT-2025 15:04:47

SQL>

And the date time of SYS

SQL> select to_char(ctime,'DD-MON-YYYY HH24:MI:SS'),ptime,exptime,ltime,spare6 from user$ where name='SYS';

TO_CHAR(CTIME,'DD-MON-YYYYHH2 PTIME EXPTIME LTIME SPARE6
----------------------------- --------- --------- --------- ---------
08-OCT-2025 22:00:58

SQL>

And all users

SQL> set lines 220
SQL> col name for a30
SQL> col ctime for a20
SQL> col ptime for a20
SQL> col exptime for a20
SQL> col ltime for a20
SQL> col spare6 for a20
SQL> select name,to_char(ctime,'DD-MON-YYYY HH24:MI:SS') ctime,to_char(ptime,'DD-MON-YYYY HH24:MI:SS') ptime,to_char(exptime,'DD-MON-YYYY HH24:MI:SS') exptime,to_char(ltime,'DD-MON-YYYY HH24:MI:SS') ltime,to_char(spare6,'DD-MON-YYYY HH24:MI:SS') spare6 from user$;

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
SYS 08-OCT-2025 22:00:58
PUBLIC 08-OCT-2025 22:00:58
CONNECT 08-OCT-2025 22:00:58
RESOURCE 08-OCT-2025 22:00:58
DBA 08-OCT-2025 22:00:58
PDB_DBA 08-OCT-2025 22:00:58
AUDIT_ADMIN 08-OCT-2025 22:00:58
AUDIT_VIEWER 08-OCT-2025 22:00:58
AUDSYS 08-OCT-2025 22:00:59 20-OCT-2025 15:13:12
SYSTEM 08-OCT-2025 22:00:59 20-OCT-2025 15:54:39
SELECT_CATALOG_ROLE 08-OCT-2025 22:00:59

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
EXECUTE_CATALOG_ROLE 08-OCT-2025 22:00:59
CAPTURE_ADMIN 08-OCT-2025 22:00:59
SYSBACKUP 08-OCT-2025 22:00:59 20-OCT-2025 15:13:12
SYSDG 08-OCT-2025 22:00:59 20-OCT-2025 15:13:12
SYSKM 08-OCT-2025 22:00:59 20-OCT-2025 15:13:12
SYSRAC 08-OCT-2025 22:00:59
OUTLN 08-OCT-2025 22:01:06 20-OCT-2025 15:13:12
EXP_FULL_DATABASE 08-OCT-2025 22:01:22
IMP_FULL_DATABASE 08-OCT-2025 22:01:22
AVTUNE_PKG_ROLE 08-OCT-2025 22:01:31
DATAPUMP_CLOUD_EXP 08-OCT-2025 22:33:17

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
VECSYS 08-OCT-2025 22:01:32 20-OCT-2025 15:13:12
BAASSYS 08-OCT-2025 22:01:33 20-OCT-2025 15:13:12
CDB_DBA 08-OCT-2025 22:31:55
APPLICATION_TRACE_VIEWER 08-OCT-2025 22:32:18
ACCHK_READ 08-OCT-2025 22:32:18
LOGSTDBY_ADMINISTRATOR 08-OCT-2025 22:32:50
DBFS_ROLE 08-OCT-2025 22:32:56
GSMUSER_ROLE 08-OCT-2025 22:32:57
GSMROOTUSER_ROLE 08-OCT-2025 22:32:57
GSMADMIN_INTERNAL 08-OCT-2025 22:32:57 20-OCT-2025 15:13:12
GSMUSER 08-OCT-2025 22:32:57 20-OCT-2025 15:13:12

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
GGSHAREDCAP 08-OCT-2025 22:32:59 20-OCT-2025 15:13:12
DIP 08-OCT-2025 22:33:07 08-OCT-2025 22:33:07
SAGA_ADM_ROLE 08-OCT-2025 22:33:12
SAGA_PARTICIPANT_ROLE 08-OCT-2025 22:33:12
SAGA_CONNECT_ROLE 08-OCT-2025 22:33:12
AQ_ADMINISTRATOR_ROLE 08-OCT-2025 22:33:14
AQ_USER_ROLE 08-OCT-2025 22:33:14
DATAPUMP_EXP_FULL_DATABASE 08-OCT-2025 22:33:17
DATAPUMP_IMP_FULL_DATABASE 08-OCT-2025 22:33:17
XS$NULL 08-OCT-2025 22:33:29 08-OCT-2025 22:33:29
DATAPUMP_CLOUD_IMP 08-OCT-2025 22:33:17

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
ADM_PARALLEL_EXECUTE_TASK 08-OCT-2025 22:33:27
PROVISIONER 08-OCT-2025 22:33:29
XS_SESSION_ADMIN 08-OCT-2025 22:33:29
XS_NAMESPACE_ADMIN 08-OCT-2025 22:33:29
XS_CACHE_ADMIN 08-OCT-2025 22:33:29
XS_CONNECT 08-OCT-2025 22:33:29
RECOVERY_CATALOG_USER 08-OCT-2025 22:36:16
REMOTE_SCHEDULER_AGENT 08-OCT-2025 22:33:34 20-OCT-2025 15:13:12
DBSFWUSER 08-OCT-2025 22:33:36 20-OCT-2025 15:13:12
SQL_FIREWALL_ADMIN 08-OCT-2025 22:33:38
SQL_FIREWALL_VIEWER 08-OCT-2025 22:33:38

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
OSAK_ADMIN_ROLE 08-OCT-2025 22:33:39
GATHER_SYSTEM_STATISTICS 08-OCT-2025 22:34:30
OPTIMIZER_PROCESSING_RATE 08-OCT-2025 22:34:30
DBMS_MDX_INTERNAL 08-OCT-2025 22:34:46
BDSQL_ADMIN 08-OCT-2025 22:35:37
BDSQL_USER 08-OCT-2025 22:35:37
RECOVERY_CATALOG_OWNER 08-OCT-2025 22:36:16
RECOVERY_CATALOG_OWNER_VPD 08-OCT-2025 22:36:16
OEM_MONITOR 08-OCT-2025 22:49:11
SYSUMF_ROLE 08-OCT-2025 22:44:53
SYS$UMF 08-OCT-2025 22:44:53 20-OCT-2025 15:13:12

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
MAINTPLAN_APP 08-OCT-2025 22:44:53
NOTIFICATIONS_USER 08-OCT-2025 22:44:55
NOTIFICATIONS_ADMIN 08-OCT-2025 22:44:55
SCHEDULER_ADMIN 08-OCT-2025 22:46:26
PPLB_ROLE 08-OCT-2025 22:46:37
DGPDB_ROLE 08-OCT-2025 22:46:37
DGPDB_INT 08-OCT-2025 22:46:37 20-OCT-2025 15:13:12
HS_ADMIN_SELECT_ROLE 08-OCT-2025 22:46:53
HS_ADMIN_EXECUTE_ROLE 08-OCT-2025 22:46:53
HS_ADMIN_ROLE 08-OCT-2025 22:46:53
GLOBAL_AQ_USER_ROLE 08-OCT-2025 22:46:56

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
OGG_CAPTURE 08-OCT-2025 22:49:10
OGG_APPLY 08-OCT-2025 22:49:10
OGG_APPLY_PROCREP 08-OCT-2025 22:49:10
OGG_SHARED_CAPTURE 08-OCT-2025 22:49:10
XSTREAM_CAPTURE 08-OCT-2025 22:49:10
XSTREAM_APPLY 08-OCT-2025 22:49:10
OEM_ADVISOR 08-OCT-2025 22:49:11
PGX_SESSION_GET_PUBLISHED_GRAP 08-OCT-2025 22:49:31
H

DBSNMP 08-OCT-2025 22:49:12 20-OCT-2025 15:13:12

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
APPQOSSYS 08-OCT-2025 22:49:12 20-OCT-2025 15:13:12
GSMCATUSER_ROLE 08-OCT-2025 22:49:14
GSMADMIN_ROLE 08-OCT-2025 22:49:14
GSM_POOLADMIN_ROLE 08-OCT-2025 22:49:14
GDS_CATALOG_SELECT 08-OCT-2025 22:49:14
SHARDED_SCHEMA_OWNER 08-OCT-2025 22:49:14
GSMCATUSER 08-OCT-2025 22:49:14 20-OCT-2025 15:13:12
GGSYS 08-OCT-2025 22:49:23 20-OCT-2025 15:13:12
GGSYS_ROLE 08-OCT-2025 22:49:23
DB_DEVELOPER_ROLE 08-OCT-2025 22:49:30
GRAPH_DEVELOPER 08-OCT-2025 22:49:31

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
GRAPH_ADMINISTRATOR 08-OCT-2025 22:49:31
GRAPH_USER 08-OCT-2025 22:49:31
PGX_SESSION_CREATE 08-OCT-2025 22:49:31
PGX_SERVER_GET_INFO 08-OCT-2025 22:49:31
PGX_SERVER_MANAGE 08-OCT-2025 22:49:31
PGX_SESSION_READ_MODEL 08-OCT-2025 22:49:31
PGX_SESSION_MODIFY_MODEL 08-OCT-2025 22:49:31
PGX_SESSION_NEW_GRAPH 08-OCT-2025 22:49:31
OJVMSYS 08-OCT-2025 23:00:15 20-OCT-2025 15:13:12
PGX_SESSION_COMPILE_ALGORITHM 08-OCT-2025 22:49:31
PGX_SESSION_ADD_PUBLISHED_GRAP 08-OCT-2025 22:49:31

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
H

PGX_SESSION_SET_IDLE_TIMEOUT 08-OCT-2025 22:49:31
XDB 08-OCT-2025 22:50:49 20-OCT-2025 15:13:12
ANONYMOUS 08-OCT-2025 22:50:49 20-OCT-2025 15:13:12
XDBADMIN 08-OCT-2025 22:50:49
XDB_SET_INVOKER 08-OCT-2025 22:51:06
AUTHENTICATEDUSER 08-OCT-2025 22:51:08
XDB_WEBSERVICES 08-OCT-2025 22:51:08
XDB_WEBSERVICES_WITH_PUBLIC 08-OCT-2025 22:51:08
XDB_WEBSERVICES_OVER_HTTP 08-OCT-2025 22:51:08

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
SODA_APP 08-OCT-2025 22:56:28
WMSYS 08-OCT-2025 22:58:20 20-OCT-2025 15:13:12
WM_ADMIN_ROLE 08-OCT-2025 22:58:24
JAVAUSERPRIV 08-OCT-2025 23:00:11
JAVAIDPRIV 08-OCT-2025 23:00:11
JAVASYSPRIV 08-OCT-2025 23:00:11
JAVADEBUGPRIV 08-OCT-2025 23:00:11
EJBCLIENT 08-OCT-2025 23:00:11
JMXSERVER 08-OCT-2025 23:00:11
DV_SECANALYST 08-OCT-2025 23:12:52
DVSYS 08-OCT-2025 23:12:46 20-OCT-2025 15:13:12

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
JAVA_ADMIN 08-OCT-2025 23:00:27
CTXSYS 08-OCT-2025 23:03:06 20-OCT-2025 15:13:12
CTXAPP 08-OCT-2025 23:03:08
OLAP_XS_ADMIN 08-OCT-2025 23:04:38
OLAPSYS 08-OCT-2025 23:04:44 20-OCT-2025 15:13:12
OLAP_DBA 08-OCT-2025 23:04:45
OLAP_USER 08-OCT-2025 23:04:45
MDSYS 08-OCT-2025 23:08:39 20-OCT-2025 15:13:12
MDDATA 08-OCT-2025 23:08:39 20-OCT-2025 15:13:12
RDFCTX_ADMIN 08-OCT-2025 23:11:11
LBACSYS 08-OCT-2025 23:12:07 20-OCT-2025 15:13:12

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
LBAC_DBA 08-OCT-2025 23:12:07
DVF 08-OCT-2025 23:12:46 20-OCT-2025 15:13:12
DV_MONITOR 08-OCT-2025 23:12:52
DV_ADMIN 08-OCT-2025 23:12:52
DV_OWNER 08-OCT-2025 23:12:52
DV_ACCTMGR 08-OCT-2025 23:12:52
DV_PATCH_ADMIN 08-OCT-2025 23:12:52
PDBADMIN 20-OCT-2025 15:13:10 20-OCT-2025 15:13:10 18-APR-2026 15:13:10
DV_STREAMS_ADMIN 08-OCT-2025 23:12:52
DV_GOLDENGATE_ADMIN 08-OCT-2025 23:12:52
DV_XSTREAM_ADMIN 08-OCT-2025 23:12:52

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
DV_GOLDENGATE_REDO_ACCESS 08-OCT-2025 23:12:52
DV_AUDIT_CLEANUP 08-OCT-2025 23:12:52
DV_DATAPUMP_NETWORK_LINK 08-OCT-2025 23:12:52
DV_POLICY_OWNER 08-OCT-2025 23:12:52
HR 20-OCT-2025 15:41:48 20-OCT-2025 15:41:48 20-OCT-2025 15:54:42
APEX_240200 20-OCT-2025 15:14:39 20-OCT-2025 15:14:39
FLOWS_FILES 20-OCT-2025 15:14:39 20-OCT-2025 15:14:39
APEX_PUBLIC_USER 20-OCT-2025 15:14:39 20-OCT-2025 15:39:31
APEX_PUBLIC_ROUTER 20-OCT-2025 15:14:40
APEX_ADMINISTRATOR_READ_ROLE 20-OCT-2025 15:14:56
APEX_ADMINISTRATOR_ROLE 20-OCT-2025 15:14:56

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
APEX_GRANTS_FOR_NEW_USERS_ROLE 20-OCT-2025 15:14:56
ORDS_PUBLIC_USER 20-OCT-2025 15:40:05 20-OCT-2025 15:40:05 01-JUN-2026 07:45:12
ORDS_METADATA 20-OCT-2025 15:40:06
ORDS_ADMINISTRATOR_ROLE 20-OCT-2025 15:40:46
ORDS_RUNTIME_ROLE 20-OCT-2025 15:40:46
_NEXT_USER 08-OCT-2025 22:00:58
SH 20-OCT-2025 15:41:50 20-OCT-2025 15:41:50 20-OCT-2025 15:54:40
CO 20-OCT-2025 15:41:52 20-OCT-2025 15:41:52 20-OCT-2025 15:54:42
HRREST 20-OCT-2025 15:48:33 20-OCT-2025 15:48:33 20-OCT-2025 15:48:39
AV 20-OCT-2025 15:48:35 20-OCT-2025 15:48:35 20-OCT-2025 15:48:35
AOS_GGADMIN 05-JUN-2026 14:22:00 05-JUN-2026 14:22:00

NAME CTIME PTIME EXPTIME LTIME SPARE6
------------------------------ -------------------- -------------------- -------------------- -------------------- --------------------
AOS_DATA 05-JUN-2026 14:43:36 05-JUN-2026 14:43:36 05-JUN-2026 14:43:40 05-JUN-2026 14:43:37
AOS_CODE 05-JUN-2026 14:43:36 05-JUN-2026 14:43:36 05-JUN-2026 14:44:54
AOS_BUILD_DATA 05-JUN-2026 14:43:36
AOS_BUILD_CODE 05-JUN-2026 14:43:36
AOS_CONNECT 05-JUN-2026 14:43:36
AOS_RUNTIME_ADMIN 05-JUN-2026 14:43:40
AOS_RUNTIME_VIEWER 05-JUN-2026 14:43:40
AOS_ADMIN 05-JUN-2026 14:43:40 05-JUN-2026 14:43:40 05-JUN-2026 14:45:30
AOS_VIEWER 05-JUN-2026 14:43:40 05-JUN-2026 14:43:40

181 rows selected.

SQL>

We can also check data files:

SQL> col dt for a20
SQL> col file_name for a50
SQL> set lines 220
SQL> select to_char(creation_time,'DD-MON-YYYY HH24:MI:SS') dt,file_name from v$datafile,dba_data_files where file#=file_id;

DT FILE_NAME
-------------------- --------------------------------------------------
20-OCT-2025 15:13:06 /opt/oracle/oradata/FREE/FREEPDB1/sysaux01.dbf
20-OCT-2025 15:13:54 /opt/oracle/oradata/FREE/FREEPDB1/users01.dbf
20-OCT-2025 15:13:06 /opt/oracle/oradata/FREE/FREEPDB1/system01.dbf
20-OCT-2025 15:13:07 /opt/oracle/oradata/FREE/FREEPDB1/undotbs01.dbf

SQL>

And the filenames:

SQL> col dt for 9999999999
SQL> col file_name for a50
SQL> set lines 220
SQL> select checkpoint_change# dt,file_name from v$datafile_header,dba_data_files where file#=file_id;

DT FILE_NAME
----------- --------------------------------------------------
4736767 /opt/oracle/oradata/FREE/FREEPDB1/sysaux01.dbf
4736767 /opt/oracle/oradata/FREE/FREEPDB1/users01.dbf
4736767 /opt/oracle/oradata/FREE/FREEPDB1/system01.dbf
4736767 /opt/oracle/oradata/FREE/FREEPDB1/undotbs01.dbf

SQL>

In summary, we cannot get a timestamp from the database for a row in a table that does not itself have a timestamp column unless we also have audit or we can use redo logs. We can use SCN but only at the block level and we could also do ID analysis to see what rows are missing but not what is in those rows. We could use off line data block analysis but unless a row was held in the ITL (Interested Transaction List) in the data block we would not get much futher.

SCNs are useful and can create an outline / boundary for instance if the last record of the 3rd block has an SCN and we can estimate a timestamp for the closed block we can estimate the time the block has existed and whether it falls into the range of the attack

#oracleace #sym_42 #oracle #forensics #database #security #scn #hacking #databreach

DV_SECANALYST Analyse Database Vault Views

I have been involved with Oracle Database Vault recently with a deployment and I was asked how can someone view the database vault dictionary views when DV is installed.

So, if we start with no DV enabled in our database and we will focus on the view DBA_DV_FACTOR. With no Database Vault we can connect as SYSDBA and see the view:

SQL> select name from dba_dv_factor;

NAME
--------------------------------------------------------------------------------
Database_Hostname
Database_IP
Database_Instance
Client_IP
Authentication_Method
Identification_Type
Database_Domain
Database_Name
Lang
Language
Network_Protocol

NAME
--------------------------------------------------------------------------------
Proxy_User
Proxy_Enterprise_Identity
Session_User
Domain
Machine
Enterprise_Identity
DV$_Module
DV$_Client_Identifier
DV$_Dblink_Info

20 rows selected.

SQL>

SYSDBA can see the contents of this view. Who owns the view:

SQL> select owner from dba_views where view_name='DBA_DV_FACTOR';

OWNER
--------------------------------------------------------------------------------
DVSYS

SQL>

What permissions are granted on this view:

SQL> select grantee,privilege from dba_tab_privs where table_name='DBA_DV_FACTOR';

GRANTEE
--------------------------------------------------------------------------------------------------------------------------------
PRIVILEGE
----------------------------------------
DV_SECANALYST
SELECT


1 row selected.

SQL>

Check who has been granted DV_SECANALYST:

SQL> select grantee,admin_option from dba_role_privs where granted_role='DV_SECANALYST';

GRANTEE
--------------------------------------------------------------------------------------------------------------------------------
ADM
---
DV_ADMIN
NO

DV_OWNER
YES


2 rows selected.

SQL>

Only DV_OWNER and DV_ADMIN roles have been granted access to this DV_SECANALYST role and DV_OWNER can grant it on. How did SYS access the DBA_DV_FACTOR view:

SQL> create user test_dv identified by test_dv;

User created.

SQL> grant create session, select any table to test_dv;

Grant succeeded.

SQL> connect test_dv/test_dv@//192.168.56.33:1539/xepdb1
Connected.
SQL> select name from dba_dv_factor;

NAME
--------------------------------------------------------------------------------------------------------------------------------
Database_Hostname
Database_IP
Database_Instance
Client_IP
Authentication_Method
Identification_Type
Database_Domain
Database_Name
Lang
Language
Network_Protocol
Proxy_User
Proxy_Enterprise_Identity
Session_User
Domain
Machine
Enterprise_Identity
DV$_Module
DV$_Client_Identifier
DV$_Dblink_Info

20 rows selected.

SQL>

Clearly the access to this view is through SELECT ANY TABLE. I will now enable DV in the CDB and PDB as as there are a lot of steps I will resume after the enable completes:

...
SQL> select * from dba_dv_status;

NAME STATUS
------------------- --------------
DV_CONFIGURE_STATUS TRUE
DV_ENABLE_STATUS TRUE
DV_APP_PROTECTION NOT CONFIGURED

SQL>
SQL> select * from dba_ols_status;

NAME STATU
-------------------- -----
DESCRIPTION
------------------------------------------------------------------------------------------------------------------------
OLS_CONFIGURE_STATUS TRUE
Determines if OLS is configured

OLS_DIRECTORY_STATUS FALSE
Determines if OID is enabled with OLS

OLS_ENABLE_STATUS TRUE
Determines if OLS is enabled


SQL>
...

Database Vault is installed. Let us try and access the DBA_DV_FACTOR view as SYSDBA:

SQL> connect sys/oracle1@//192.168.56.33:1539/xepdb1 as sysdba
Connected.
SQL> select name from dba_dv_factor;
select name from dba_dv_factor
*
ERROR at line 1:
ORA-01031: insufficient privileges


SQL>

No luck, what if i grant DV_SECANALYST to SYSDBA:

SQL> grant dv_secanalyst to sys;
grant dv_secanalyst to sys
*
ERROR at line 1:
ORA-47410: Realm violation for GRANT on DV_SECANALYST


SQL> connect c##dvo/oracle1@//192.168.56.33:1539/xepdb1
Connected.
SQL> grant dv_secanalyst to sys;

Grant succeeded.

SQL> connect sys/oracle1@//192.168.56.33:1539/xepdb1 as sysdba
Connected.
SQL> select name from dba_dv_factor;
select name from dba_dv_factor
*
ERROR at line 1:
ORA-01031: insufficient privileges


SQL>

So, SYS cannot grant DV roles, so we must use DVO to do this BUT when we connect again as SYS it still cannot access the DBA_DV_FACTOR view as it is blocked by DV even when it has the correct role.

What if we grant DV_SECANALYST to our test user:

SQL> connect test_dv/test_dv@//192.168.56.33:1539/xepdb1
Connected.
SQL> select name from dba_dv_factor;
select name from dba_dv_factor
*
ERROR at line 1:
ORA-01031: insufficient privileges


SQL> connect c##dvo/oracle1@//192.168.56.33:1539/xepdb1
Connected.
SQL> grant dv_secanalyst to test_dv;

Grant succeeded.

SQL> connect test_dv/test_dv@//192.168.56.33:1539/xepdb1
Connected.
SQL> select name from dba_dv_factor;

NAME
------------------------------------------------------------------------------------------------------------------------
Database_Hostname
Database_IP
Database_Instance
Client_IP
Authentication_Method
Identification_Type
Database_Domain
Database_Name
Lang
Language
Network_Protocol
Proxy_User
Proxy_Enterprise_Identity
Session_User
Domain
Machine
Enterprise_Identity
DV$_Module
DV$_Client_Identifier
DV$_Dblink_Info

20 rows selected.

SQL>

It works. So, in a vanilla DV install a user with SELECT ANY TABLE cannot see DBA_DV_FACTOR but if granted the DV_SECANALYST role it can.

Maintenance is more complex with DV enabled as we need DV_SECANALYST to see the DV views and we cannot see them with SYSDBA even when it has this role. We also must use a DVO user to make the grants and a DV_ACCTMGR user to create and maintain users. This is separation of duties.

#oracleace #sym_42 #oracle #databasevault #vault #dv #dv_secanalyst

I forgot my Oracle Database Vault owner Password

Let us imagine that I have a 21c database that has Database Vault (DV) enabled and I have forgotten my Database Vault owner password. In my case C##DVO. This user as well as a backup (That I have also forgotten the password for) is needed to disable or enable database vault. Yes, the database can still be used and can be stopped and re-started with SYDBA but what if there is also an error in the Database Vault then potentially I need to be able to login with a user that has DV_OWNER to be able to disable DV and fix the issue and also to be sure that I can fully support this database.

Even though I have the password for a user with the DV_ACCTMGR role this cannot be used to change the DVO password. Only the DVO can change their own password as DV protects itself by not allowing the password of the owner to be changed except by the owner and not even by the ACCTMGR user.

This behaviour is intentional from Oracle to make it very hard to disable DV if you do not have access to a DV OWNER.

So, if we have got in this state - we forgot the DV OWNER password then what can we do?

Oracle states that it cannot be recovered or fixed. It suggests that we "find" the password in external password vaults, pieces of paper, in files lying around or indeed any way you might find a file; alternately Oracle also suggests checking for other users with the DV_OWNER role to instead use them.

Alternately I have read that Oracle may be able to help customers via an SR in this case. I also read (with no proof) that Oracle are able to set events during the boot process so that between MOUNT and OPEN and after the bootstrap and dictionary loaded so that DV can be disabled without the DV ONWER password. I have no idea if this is true or not as I cannot find anything on Google.

Lets explore some of the ideas on the internet. Someone suggested that a STARTUP UPGRADE could bypass DV and allow the DVO password to be changed or DV to be disabled:

root@192.168.56.33's password:
Last login: Tue May 26 07:32:23 2026 from 192.168.56.1
[root@ol21cxe ~]# su - oracle
Last login: Tue May 26 07:32:36 BST 2026 on pts/1
[oracle@ol21cxe ~]$ source /opt/oracle/product/21c/dbhomeXE/bin/oraenv
ORACLE_SID = [oracle] ? XE
The Oracle base has been set to /opt/oracle
[oracle@ol21cxe ~]$ sqlplus / as sysdba

SQL*Plus: Release 21.0.0.0.0 - Production on Wed May 27 13:36:11 2026
Version 21.3.0.0.0

Copyright (c) 1982, 2021, Oracle. All rights reserved.


Connected to:
Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0

SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup upgrade
ORACLE instance started.

Total System Global Area 1157625952 bytes
Fixed Size 9685088 bytes
Variable Size 905969664 bytes
Database Buffers 234881024 bytes
Redo Buffers 7090176 bytes
Database mounted.
Database opened.
SQL> exec dbms_macadm.disable_dv;
BEGIN dbms_macadm.disable_dv; END;

*
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00904: insufficient privilege to access object DVSYS.DBMS_MACADM
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored


SQL> grant execute on dvsys.dbms_macadm to sys;

grant execute on dvsys.dbms_macadm to sys
*
ERROR at line 1:
ORA-47401: Realm violation for GRANT on DVSYS.DBMS_MACADM


SQL> alter user c##dvo identified by oracle1;
alter user c##dvo identified by oracle1
*
ERROR at line 1:
ORA-01031: insufficient privileges


SQL>

So, no, that idea does not work. Another idea on the internet is to shutdown the database and unlink DV in the binaries. Let us try that:

SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> exit
Disconnected from Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0
[oracle@ol21cxe ~]$ cd $ORACLE_HOME/rdbms/lib
[oracle@ol21cxe lib]$ make -f ins_rdbms.mk dv_off ioracle
dv_off has been deprecated
chmod 755 /opt/oracle/product/21c/dbhomeXE/bin
/usr/bin/ar: creating /opt/oracle/product/21c/dbhomeXE/lib/libserver.a

- Linking Oracle
rm -f /opt/oracle/product/21c/dbhomeXE/rdbms/lib/oracle
rm -f /opt/oracle/product/21c/dbhomeXE/rdbms/lib/oracle.map; /opt/oracle/product/21c/dbhomeXE/bin/orald -o /opt/oracle/product/21c/dbhomeXE/rdbms/lib/oracle -m64 -z noexecstack -Wl,--disable-new-dtags -L/opt/oracle/product/21c/dbhomeXE/rdbms/lib/ -L/opt/oracle/product/21c/dbhomeXE/lib/ -L/opt/oracle/product/21c/dbhomeXE/lib/stubs/ -Wl,-Map,/opt/oracle/product/21c/dbhomeXE/rdbms/lib/oracle.map -Wl,-E /opt/oracle/product/21c/dbhomeXE/rdbms/lib/opimai.o /opt/oracle/product/21c/dbhomeXE/rdbms/lib/ssoraed.o /opt/oracle/product/21c/dbhomeXE/rdbms/lib/ttcsoi.o -Wl,--whole-archive -lperfsrv -Wl,--no-whole-archive /opt/oracle/product/21c/dbhomeXE/lib/nautab.o /opt/oracle/product/21c/dbhomeXE/lib/naeet.o /opt/oracle/product/21c/dbhomeXE/lib/naect.o /opt/oracle/product/21c/dbhomeXE/lib/naedhs.o /opt/oracle/product/21c/dbhomeXE/rdbms/lib/config.o -ldmext -lserver -loraodm -lofs -lcellst -lnnet -lskgxp -lsnls -lnls -lcore -lsnls -lnls -lcore -lsnls -lnls -lxml -lcore -lunls -lsnls -lnls -lcore -lnls -lclient -lvsnst -lcommon -lgeneric -lknlopt -loraolap -lskjcx -lslax -lpls -lrt -lplp -ldmext -lserver -lclient -lvsnst -lcommon -lgeneric `if [ -f /opt/oracle/product/21c/dbhomeXE/lib/libavserver.a ] ; then echo "-lavserver" ; else echo "-lavstub"; fi` `if [ -f /opt/oracle/product/21c/dbhomeXE/lib/libavclient.a ] ; then echo "-lavclient" ; fi` -lknlopt -lslax -lpls -lrt -lplp -ljavavm -lserver -lwwg `cat /opt/oracle/product/21c/dbhomeXE/lib/ldflags` -lncrypt -lnsgr -ln -lnl -lngsmshd -lnro `cat /opt/oracle/product/21c/dbhomeXE/lib/ldflags` -lncrypt -lnsgr -ln -lnl -lngsmshd -lnnzst -lzt -lztkg -lmm -lsnls -lnls -lcore -lsnls -lnls -lcore -lsnls -lnls -lxml -lcore -lunls -lsnls -lnls -lcore -lnls -lztkg `cat /opt/oracle/product/21c/dbhomeXE/lib/ldflags` -lncrypt -lnsgr -ln -lnl -lngsmshd -lnro `cat /opt/oracle/product/21c/dbhomeXE/lib/ldflags` -lncrypt -lnsgr -ln -lnl -lngsmshd -lnnzst -lzt -lztkg -lsnls -lnls -lcore -lsnls -lnls -lcore -lsnls -lnls -lxml -lcore -lunls -lsnls -lnls -lcore -lnls `if /usr/bin/ar tv /opt/oracle/product/21c/dbhomeXE/rdbms/lib/libknlopt.a | grep "kxmnsd.o" > /dev/null 2>&1 ; then echo " " ; else echo "-lordsdo -lserver"; fi` -L/opt/oracle/product/21c/dbhomeXE/lib/ -lctxc -lctx -lzx -lgx -lctx -lzx -lgx -lclscest -loevm -lclsra -ldbcfg -lhasgen -lnnzst -lzt -lxml -lgeneric -locr -locrb -locrutl -lhasgen -lnnzst -lzt -lxml -lgeneric -lgeneric -lorazip -loraz -llzopro5 -lorabz2 -lorazstd -loralz4 -lipp_z -lipp_bz2 -lippdc -lipps -lippcore -lippcp -lsnls -lnls -lcore -lsnls -lnls -lcore -lsnls -lnls -lxml -lcore -lunls -lsnls -lnls -lcore -lnls -lsnls -lunls -llxled -lsnls -lnls -lcore -lsnls -lnls -lcore -lsnls -lnls -lxml -lcore -lunls -lsnls -lnls -lcore -lnls -lasmclnt -lcommon -lcore -ledtn -laio -lons -lmql1 -lipc1 -lmql1 -lipc1 -lfthread `cat /opt/oracle/product/21c/dbhomeXE/lib/sysliblist` -Wl,-rpath,/opt/oracle/product/21c/dbhomeXE/lib -lm `cat /opt/oracle/product/21c/dbhomeXE/lib/sysliblist` -ldl -lm -L/opt/oracle/product/21c/dbhomeXE/lib `test -x /usr/bin/hugeedit -a -r /usr/lib64/libhugetlbfs.so && test -r /opt/oracle/product/21c/dbhomeXE/rdbms/lib/shugetlbfs.o && echo -Wl,-zcommon-page-size=2097152 -Wl,-zmax-page-size=2097152 -lhugetlbfs`
/usr/bin/ar: /opt/oracle/product/21c/dbhomeXE/rdbms/lib/libknlopt.a: No such file or directory
/usr/bin/ld: cannot find /opt/oracle/product/21c/dbhomeXE/lib/crtbegin.o: No such file or directory
/usr/bin/ld: cannot find /opt/oracle/product/21c/dbhomeXE/rdbms/lib/opimai.o: No such file or directory
/usr/bin/ld: cannot find /opt/oracle/product/21c/dbhomeXE/rdbms/lib/ssoraed.o: No such file or directory
/usr/bin/ld: cannot find /opt/oracle/product/21c/dbhomeXE/rdbms/lib/ttcsoi.o: No such file or directory
/usr/bin/ld: cannot find -lperfsrv
/usr/bin/ld: cannot find /opt/oracle/product/21c/dbhomeXE/lib/nautab.o: No such file or directory
/usr/bin/ld: cannot find /opt/oracle/product/21c/dbhomeXE/lib/naeet.o: No such file or directory
/usr/bin/ld: cannot find /opt/oracle/product/21c/dbhomeXE/lib/naect.o: No such file or directory
/usr/bin/ld: cannot find /opt/oracle/product/21c/dbhomeXE/lib/naedhs.o: No such file or directory
/usr/bin/ld: cannot find -ldmext
/usr/bin/ld: cannot find -loraodm
/usr/bin/ld: cannot find -lcellst
/usr/bin/ld: cannot find -lnnet
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lclient
/usr/bin/ld: cannot find -lvsnst
/usr/bin/ld: cannot find -lcommon
/usr/bin/ld: cannot find -lgeneric
/usr/bin/ld: cannot find -lknlopt
/usr/bin/ld: cannot find -loraolap
/usr/bin/ld: cannot find -lslax
/usr/bin/ld: cannot find -lpls
/usr/bin/ld: cannot find -lplp
/usr/bin/ld: cannot find -ldmext
/usr/bin/ld: cannot find -lclient
/usr/bin/ld: cannot find -lvsnst
/usr/bin/ld: cannot find -lcommon
/usr/bin/ld: cannot find -lgeneric
/usr/bin/ld: cannot find -lavstub
/usr/bin/ld: cannot find -lknlopt
/usr/bin/ld: cannot find -lslax
/usr/bin/ld: cannot find -lpls
/usr/bin/ld: cannot find -lplp
/usr/bin/ld: cannot find -ljavavm
/usr/bin/ld: cannot find -lwwg
/usr/bin/ld: cannot find -lnbeq
/usr/bin/ld: cannot find -lntmq
/usr/bin/ld: cannot find -lnhost
/usr/bin/ld: cannot find -lnus
/usr/bin/ld: cannot find -lnldap
/usr/bin/ld: cannot find -lldapclnt
/usr/bin/ld: cannot find -lngsmshd
/usr/bin/ld: cannot find -lntcp
/usr/bin/ld: cannot find -lntcps
/usr/bin/ld: cannot find -lntcp
/usr/bin/ld: cannot find -lntns
/usr/bin/ld: cannot find -lntwss
/usr/bin/ld: cannot find -lncrypt
/usr/bin/ld: cannot find -lnsgr
/usr/bin/ld: cannot find -ln
/usr/bin/ld: cannot find -lnl
/usr/bin/ld: cannot find -lngsmshd
/usr/bin/ld: cannot find -lnro
/usr/bin/ld: cannot find -lnbeq
/usr/bin/ld: cannot find -lntmq
/usr/bin/ld: cannot find -lnhost
/usr/bin/ld: cannot find -lnus
/usr/bin/ld: cannot find -lnldap
/usr/bin/ld: cannot find -lldapclnt
/usr/bin/ld: cannot find -lngsmshd
/usr/bin/ld: cannot find -lntcp
/usr/bin/ld: cannot find -lntcps
/usr/bin/ld: cannot find -lntcp
/usr/bin/ld: cannot find -lntns
/usr/bin/ld: cannot find -lntwss
/usr/bin/ld: cannot find -lncrypt
/usr/bin/ld: cannot find -lnsgr
/usr/bin/ld: cannot find -ln
/usr/bin/ld: cannot find -lnl
/usr/bin/ld: cannot find -lngsmshd
/usr/bin/ld: cannot find -lnnzst
/usr/bin/ld: cannot find -lzt
/usr/bin/ld: cannot find -lztkg
/usr/bin/ld: cannot find -lmm
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lztkg
/usr/bin/ld: cannot find -lnbeq
/usr/bin/ld: cannot find -lntmq
/usr/bin/ld: cannot find -lnhost
/usr/bin/ld: cannot find -lnus
/usr/bin/ld: cannot find -lnldap
/usr/bin/ld: cannot find -lldapclnt
/usr/bin/ld: cannot find -lngsmshd
/usr/bin/ld: cannot find -lntcp
/usr/bin/ld: cannot find -lntcps
/usr/bin/ld: cannot find -lntcp
/usr/bin/ld: cannot find -lntns
/usr/bin/ld: cannot find -lntwss
/usr/bin/ld: cannot find -lncrypt
/usr/bin/ld: cannot find -lnsgr
/usr/bin/ld: cannot find -ln
/usr/bin/ld: cannot find -lnl
/usr/bin/ld: cannot find -lngsmshd
/usr/bin/ld: cannot find -lnro
/usr/bin/ld: cannot find -lnbeq
/usr/bin/ld: cannot find -lntmq
/usr/bin/ld: cannot find -lnhost
/usr/bin/ld: cannot find -lnus
/usr/bin/ld: cannot find -lnldap
/usr/bin/ld: cannot find -lldapclnt
/usr/bin/ld: cannot find -lngsmshd
/usr/bin/ld: cannot find -lntcp
/usr/bin/ld: cannot find -lntcps
/usr/bin/ld: cannot find -lntcp
/usr/bin/ld: cannot find -lntns
/usr/bin/ld: cannot find -lntwss
/usr/bin/ld: cannot find -lncrypt
/usr/bin/ld: cannot find -lnsgr
/usr/bin/ld: cannot find -ln
/usr/bin/ld: cannot find -lnl
/usr/bin/ld: cannot find -lngsmshd
/usr/bin/ld: cannot find -lnnzst
/usr/bin/ld: cannot find -lzt
/usr/bin/ld: cannot find -lztkg
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lordsdo
/usr/bin/ld: cannot find -lctxc
/usr/bin/ld: cannot find -lctx
/usr/bin/ld: cannot find -lzx
/usr/bin/ld: cannot find -lgx
/usr/bin/ld: cannot find -lctx
/usr/bin/ld: cannot find -lzx
/usr/bin/ld: cannot find -lgx
/usr/bin/ld: cannot find -lclscest
/usr/bin/ld: cannot find -lnnzst
/usr/bin/ld: cannot find -lzt
/usr/bin/ld: cannot find -lgeneric
/usr/bin/ld: cannot find -lnnzst
/usr/bin/ld: cannot find -lzt
/usr/bin/ld: cannot find -lgeneric
/usr/bin/ld: cannot find -lgeneric
/usr/bin/ld: cannot find -lorazip
/usr/bin/ld: cannot find -loraz
/usr/bin/ld: cannot find -llzopro5
/usr/bin/ld: cannot find -lorabz2
/usr/bin/ld: cannot find -lorazstd
/usr/bin/ld: cannot find -loralz4
/usr/bin/ld: cannot find -lipp_z
/usr/bin/ld: cannot find -lipp_bz2
/usr/bin/ld: cannot find -lippdc
/usr/bin/ld: cannot find -lipps
/usr/bin/ld: cannot find -lippcore
/usr/bin/ld: cannot find -lippcp
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -llxled
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lsnls
/usr/bin/ld: cannot find -lasmclnt
/usr/bin/ld: cannot find -lcommon
/usr/bin/ld: cannot find -ledtn
/usr/bin/ld: cannot find -lfthread
make: *** [/opt/oracle/product/21c/dbhomeXE/rdbms/lib/oracle] Error 1

That does not work either as the line "dv_off has been deprecated" shows that you cannot unlink the software anymore to disable DV. Another idea suggested by someone was to create a "pfile"; set the parameter enable_dv=false and then open the database. No one else suggested this but let us try for completeness:

SQL> create pfile='/tmp/initxe.ora' from spfile;

File created.

SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL>
SQL> exit
[oracle@ol21cxe lib]$ cat /tmp/initxe.ora
XE.__data_transfer_cache_size=0
XE.__db_cache_size=167772160
XE.__inmemory_ext_roarea=0
XE.__inmemory_ext_rwarea=0
XE.__java_pool_size=184549376
XE.__large_pool_size=16777216
XE.__oracle_base='/opt/oracle'#ORACLE_BASE set from environment
XE.__pga_aggregate_target=385875968
XE.__sga_target=1157627904
XE.__shared_io_pool_size=67108864
XE.__shared_pool_size=671088640
XE.__streams_pool_size=33554432
XE.__unified_pga_pool_size=0
*.audit_file_dest='/opt/oracle/admin/XE/adump'
*.audit_trail='db'
*.compatible='21.0.0'
*.control_files='/opt/oracle/oradata/XE/control01.ctl','/opt/oracle/oradata/XE/control02.ctl'
*.db_block_size=8192
*.db_name='XE'
*.diagnostic_dest='/opt/oracle'
*.dispatchers='(PROTOCOL=TCP) (SERVICE=XEXDB)'
*.enable_pluggable_database=true
*.local_listener='LISTENER_XE'
*.nls_language='ENGLISH'
*.nls_territory='UNITED KINGDOM'
*.open_cursors=300
*.permit_92_wrap_format=TRUE
*.pga_aggregate_target=364m
*.processes=300
*.remote_login_passwordfile='EXCLUSIVE'
*.sga_target=1093m
*.undo_tablespace='UNDOTBS1'
*.enable_dv=false
[oracle@ol21cxe lib]$ sqlplus / as sysdba

SQL> startup pfile='/tmp/initxe.ora';
LRM-00101: unknown parameter name 'enable_dv'
ORA-01078: failure in processing system parameters
SQL>

I knew this would not work as no other sites suggested it. A quick check in the database shows this parameter does not exist and also a detailed check of all parameters including hidden underscore ones and none relate to disabling DV.

Are there any other DV_ONWER users that we may know the password of:

SQL> col grantee for a30
SQL> col granted_role for a30

SQL> select grantee, granted_role, common from dba_role_privs where granted_role in ('DV_OWNER','DV_ACCTMGR');

GRANTEE GRANTED_ROLE COM
------------------------------ ------------------------------ ---
C##ACCO DV_ACCTMGR NO
C##ACCO_BK DV_ACCTMGR NO
C##DVO_BK DV_OWNER NO
C##DVO DV_OWNER NO
C##ACCO_BK DV_ACCTMGR YES
C##DVO_BK DV_OWNER YES

6 rows selected.

SQL>

The backup users have the roles granted COMMON and each user also has the roles granted locally (backup and non backup) so if we knew the password of C##DVO_BK then we could get in with that and disable DV and then change the C##DVO password to a known value.

Can a user with DV_ACCTMGR change the password of a user with DV_OWNER?

SQL> connect c##acco/oracle1@//192.168.56.33:1539/xe
Connected.
SQL> alter user c##dvo identified by oracle1;
alter user c##dvo identified by oracle1
*
ERROR at line 1:
ORA-01031: insufficient privileges


SQL> alter user c##dvo_bk identified by oracle1;
alter user c##dvo_bk identified by oracle1
*
ERROR at line 1:
ORA-01031: insufficient privileges


SQL>

Only DV_OWNER can change their own password, they cannot change the password of another user with DV_OWNER. DV_ACCTMGR can change other passwords in the database though but a user with DV_OWNER cannot change the password of a user with DV_ACCTMGR:

SQL> connect c##dvo_bk/oracle1@//192.168.56.33:1539/xe
Connected.
SQL> alter user c##dvo identified by oracle1;
alter user c##dvo identified by oracle1
*
ERROR at line 1:
ORA-01031: insufficient privileges


SQL>

only DVO can change his own password. BTW, other users can still change their own passwords even with password restrictions out of the box OOTB in DV

SQL> alter user c##dvo identified by oracle1;

User altered.

SQL>

Of course I have not really forgotten the C##DVO password I am just pretending I have for testing, hence i can demonstrate re-setting the DV_OWNER password above.

Let us see if we generate the password hash of a know password from another database can we re-apply it to our database when the password is not known:

SQL> connect sys/oracle1@//192.168.56.33:1539/xe as sysdba
Connected.
SQL> set long 1000000
SQL> select dbms_metadata.get_ddl('USER','C##DVO') from dual;

DBMS_METADATA.GET_DDL('USER','C##DVO')
--------------------------------------------------------------------------------

CREATE USER "C##DVO" IDENTIFIED BY VALUES 'S:680448D96099E877FE4A3A5938001775
D8284A41ACA3732A67B45193E287;T:40B680A3A8453A1F0AAC83EAA3FB8D05764C155527B3B8A21
FDB01E9F18F9FE868CF3BBBC4246205F09E66B0B6062CB22402614CC106A83118206BB784C09BFFF
AD97B9689B57687666E0DFA2332CF75'
DEFAULT TABLESPACE "USERS"
TEMPORARY TABLESPACE "TEMP"


SQL>

One method that would work to recover the password providing its short enough or a dictionary password is to crack it:

SQL> sho user
USER is "SYS"
SQL> grant select on sys.user$ to system;

Grant succeeded.

SQL> connect system/oracle1@//192.168.56.33:1539/xe
ERROR:
ORA-28002: the password will expire within 7 days


Connected.
SQL> alter user system identified by oracle1;

User altered.

SQL>

PFCLScan 2026 Password Cracker



Let us try another method. We can attempt to update SYS.USER$ directly with SQL. This is not supported and is only for research and entertainment:

SQL> connect sys/oracle1@//192.168.56.33:1539/xe as sysdba
Connected.
SQL> update sys.user$ set spare4='S:680448D96099E877FE4A3A5938001775
2 D8284A41ACA3732A67B45193E287;T:40B680A3A8453A1F0AAC83EAA3FB8D05764C155527B3B8A21
3 FDB01E9F18F9FE868CF3BBBC4246205F09E66B0B6062CB22402614CC106A83118206BB784C09BFFF
4 AD97B9689B57687666E0DFA2332CF75' where name='C##DVO';
update sys.user$ set spare4='S:680448D96099E877FE4A3A5938001775
*
ERROR at line 1:
ORA-01031: insufficient privileges


SQL>

Did not work, can we use ALTER USER identified by values:

SQL> connect sys/oracle1@//192.168.56.33:1539/xe as sysdba
Connected.
SQL> alter user c##dvo identified by values 'S:680448D96099E877FE4A3A5938001775
2 D8284A41ACA3732A67B45193E287;T:40B680A3A8453A1F0AAC83EAA3FB8D05764C155527B3B8A21
3 FDB01E9F18F9FE868CF3BBBC4246205F09E66B0B6062CB22402614CC106A83118206BB784C09BFFF
4 AD97B9689B57687666E0DFA2332CF75';
alter user c##dvo identified by values 'S:680448D96099E877FE4A3A5938001775
*
ERROR at line 1:
ORA-01031: insufficient privileges


SQL>

What about STARTUP MIGRATE?

SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup migrate
ORACLE instance started.

Total System Global Area 1157625952 bytes
Fixed Size 9685088 bytes
Variable Size 905969664 bytes
Database Buffers 234881024 bytes
Redo Buffers 7090176 bytes
Database mounted.
Database opened.
SQL> sho parameter cluster_database

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
cluster_database boolean FALSE
SQL> alter user c##dvo identified by oracle1;
alter user c##dvo identified by oracle1
*
ERROR at line 1:
ORA-01031: insufficient privileges


SQL>

The only viable solution found seems to be to try and crack the password or find the password on paper, file or externally. If the password is simple and a dictionary word then cracking would work. If it is long and complex then this is likely not practical either as cracking would take a lot of resource or not be possible if the key space is too large

So, back to the beginning; someone suggested that Oracle via an SR can interrupt the boot process between STARTUP MOUNT and OPEN and after bootstrap whereby DV can be disabled. I really do not know if this is true or not; it might be otherwise Oracle would be telling customers to rebuild and potentially lose data.

There is one final method that I am not going to demo and it would not be supported. Almost 20 years ago in this blog I talked in a post How to hack SYS password without logging into the database. This post referenced papers by Miladin Modrakovic and Graham Thornton who talked about using BBED to change the SYS password in the datafile

This still makes sense to me as an unsupported solution. If the database is shutdown then DV is not protecting it. If we were able to change the C##DVO password directly in the data file and fix the CRC - checksum then this would get around the lost DVO password. Its a thought experiment of course and would not be supported by Oracle!!

#oracleace #sym_42 #oracle #security #hacking #database #vault #password #lost #cracking

World Password Day and Oracle Security

I am slightly late with this one as the event itself was on the 7th May 2026. The World Password Day 2026 is a day to try and highlight that passwords are weak. An article I saw on line said that 48% of passwords can be cracked in less than 1 minute and another article said that 68% of passwords can be cracked in one day. OK, this has to be by survey and no one tried to crack all passwords in the world to come up with figures of 48% and 68% but its reflective.

The purpose of world password day is a call to get people to stop treating passwords as the perimeter to systems and the to shift identity as the perimeter and reduce reliance on passwords. Use pass phrases that are more than 25 characters and start to adopt MFA and pass keys and also to make behaviour and risk part of the logon process.

A good example in Oracle would be to use a logon trigger or audit trails and reporting analysis to show patterns of why connects, from where and when and with what.

Obviously in Oracle implement password profiles and a verify function. MFA in the same way as twitter or facebook or linkedin or ... where you are sent a text is possible with various features from Oracle that cost extra but from 23.9 RU and 19.28 MFA is supported from the database as a core feature.

You can / could create a sort of MFA in the standard database by using various factors to check if the user not only has the password but conforms to other factors such as program, location, program, ... I know this is not MFA but we could verify a user not only on password. If we create a logon trigger then these factors can be tested.

My experience over many years is that Oracle databases when reviewed often do not have password profiles, verification functions or strong passwords. I have also seen passwords that have not changed in years and not many but some for decades.

Trevor who I have known for many years has set up a password quiz to coincide with world password day and he has already had some people take part and he is looking to report any differences between the UK/EU and USA and also younger vs older people.

Please do the quiz if you have time, its simple and quick but should provide valuable, insights. Trevor will analyse the results and show on his site and I will share some of the analysis here once it is available.

#oracleace #oracle #security #world #password #day #cracking # hacking

Securing Data in Oracle without Cost Options

I did a presentation at the UKOUG conference at the East Side rooms in Birmingham at the end of 2025. The focus of this talk was to highlight the problem of securing data held in an Oracle database without using cost options from Oracle such as VPD, OLS, Database vault and more.

I have nothing against these cost options, in fact I love to work with them when customers ask me to help design and implement VPD or Database vault or others such as masking. BUT, some customers cannot justify paying for cost options or they cannot use them because they have licensed standard edition. Yes, I know VPD is included in Enterprise Edition of the database, it is just an example of the options.

This presentation covers the problem and then uses an example of the UNIFIED_AUDIT_TRAIL to show how Oracle protects this table and also showing that it is not protected using cost options. I then investigate how we might protect a table using standard features of the database. We then quickly discover that if we use one feature such as a trigger then we need to stop anyone disabling or removing or changing the trigger. Similarly if we use a grant then we need to stop anyone from changing that grant.

We cannot replicate 100% what Oracle does with cost options BUT we can get close. What we are trying to implement as an example is a read only table with some extras. The table is READ ONLY but data must be added to it in the first place. So we allow data adding BUT only through one specific interface. We must allow purging of the data so we also allow limited delete but again through a controlled interface. No UPDATE of data is allowed. We use this design scope as a basis to build a sample design and this is shown.

The whole design also should be audited to ensure any attempts to bypass the security model are caught

I implement the design as an example and then add the protections of the protections.

This is layered security. The slides for A Design Pattern To Secure Your Database are available.

#oracleace #sym_42 #oracle #security #lockdown #protection #data #hacking #data #databreach #readonly

Fuzzing PL/SQL

I have not had chance to write any detailed blogs of late as we have been very busy with paying work and also working on our software products as we released version 2025 of PFCLScan and version 2026 is very close to release also with improvements, features and more being added. As usual I do have a back log of blogs that I would like to write and release to the website. I have am internal software called PFCLSocial that I created that allows me to draft and collect blogs as I get chance to write them or spend a few minutes so that they are stored and collected in a safe place on the desktop and not online until published.

Last December I gave a presentation at the UKOUG conference in the East Side Rooms in Birmingham. This was a great conference and I feel the UKOUG conference is improving year on year. It was great to meet and chat to so many people and to give two talks.

My first talk was "Fuzzing PL/SQL" and I wanted to focus here on a different method of testing PL/SQL for security issues that is more akin to what an attacker may do to exploit your PL/SQL code. If we take a simpler (well maybe not simpler in reality but...) example of an attacker exploiting a website; they may use a tool or by had try lots and lots of URLs and different parameters and try and guess pages of the website by name and particularly look for hidden pages or authenticated pages or look for know exploits in the frame work used such as Wordpress. In the web world there are many tools that can fire thousands of requests against a website to try and find exploits in it. Also in the network security world there are tools such as "Spike" that target network listeners like the Oracle database listener to look for vulnerabilities in general to "overflow" the listener code and gain access to the Oracle or root account.

In the PL/SQL world at a simplistic level the idea is to find all the input interfaces to your PL/SQL and to run every interface many times with many types of input in an attempt to do a similar idea as "spike" or "Burpsuite" in the web world. The idea is to run each PL/SQL procedure or function and pass a lot of random (or not so random) input in an attempt to get it to fall over and find vulnerabilities. This is the idea and it is live "blind" testing in contrast to static code analysis that would normally be done.

There is one issue to raise now before you look at my slides. This is a destructive type of testing and should not be done on a production system. It is obvious, if you pass random input to every procedure and function it is bound to corrupt the database. So only use methods like this if its a test system that can be replaced.

There is a second more subtle problem. Because we are blind testing we cannot guarantee code coverage to get to all issues. This is because you may need to input one value to cause the code to do x, y or z and then within Z we need another value to get to the vulnerability.

This fuzzing method can be used along side static analysis to replicate what an attacker may do.

The slides are on my website on the Fuzzing PL/SQL Page.

#oracleace #sym_42 #oracle #security #plsql #code #vulnerabilities #vulerability #hacking #fuzzing #blindtesting

23 Years of Oracle Security

It is just slightly over 23 years since I started PeteFinnigan.com Limited to focus just on helping people secure data in Oracle databases. The company was incorporated on the 12th February 2003, so 23 years ago.

I started the company to offer services around securing Oracle and eventually to create software and training in the areas of securing Oracle databases and data. Things have changed over 23 years; when I started Oracle 9.1 was just still around, 9.2 was fully supported and 10.1 was released around the same time. Of course at that time people were still running 8.1.7 as well. In a similar pattern now some small amounts of customers still have 11.2 database and even earlier around as well as the current supported databases.

Oracle security is not done on the bleeding edge of Oracle versions as we tend to be asked to look at older supported databases such as 19c and occasionally older versions. A few years ago (probably less the 10, but cannot remember) I was asked to look at a finance application hosted out of an Oracle 6.0 database. They didn't want to upgrade as the business managed and processed in the database was winding down so it was not viable to spend a huge amount of money BUT they wanted to secure what they had. This was still possible to some extent to do quite a bit of changes to users/permissions/some parameters and more.

Oracle security has changed over the years. Most site never considered securing databases 23 years ago and had no budget for it. The Oracle database and applications and features have become much more complex BUT the core database security features have not changed massively. Yes, they have increased, there are a lot more objects. We have much more options for security now such as Database Vault, Firewall, Real Application Security and many more but the core is roughly similar except there is a huge amount of it.

When i started my company it was named after my website and this helped grow the business and get customers. The biggest changes now are that data theft has become MORE REAL. Everyone knows what it is. 23 years ago the BBC would bring on an expert to talk about some data hack or similar but now no experts, just the normal news reader will report it and the public now gets it. So why do companies treat data security as like a distant cousin? probably because there is a drive to get function, performance and security is an after thought. We can add security to any existing Oracle database but it would be much better to add it at design time.

A lot has changed at the detailed level in 23 years but a lot stays the same at the high level.

When I started the company I wanted to create software to sell to help customers secure their databases. I started PFCLScan in 2003 and the blog referenced gives some background. The first C based engine was working in 2008 and in the five years until then I created around 40k lines of PL/SQL to scan databases for customers for security issues.

Over the years PFCLScan was released as a full Windows product and we also added other products as Apps on top of PFCLScan; including PFCLObfuscate to protect PL/SQL in your database and PFCLCode to analyse the PL/SQL in a database for security issues; we also have PFCLForensics to do live response and forensic analysis of a database that may have been breached. We also have PFCLATK which is a toolkit of PL/SQL that can provide policy based auditing in the database not just limited to the Oracle audit trails. We are working on more products that will be released this year.

Version 2025 of PFCLScan was released in February this year and we are working on version 2026 now.

So, we have three streams in PeteFinnigan.com Limited. The first is the software products to help customers secure Oracle; we have 10 days of expert training classes on all areas of Oracle security that i occasionally offer from our office in York and also live via video conferencing; The third is any and all aspects of consulting around Oracle security. We cover anything from audits, hardening, designing securing solutions, Oracle cost options, encryption and more. Basically anything related to Oracle security.

One other area I occasionally get involved in is search and web promotion. We have been doing this for years on this website and I have gained a lot of knowledge in this area. We get just less than 10,000 visits per day on average to this web site and have around 50k social follows / connections. I developed a tool to do cookie audits when we have a lot of domains. We sell the cookie tool as its now an APP in PFCLScan. I also developed tools to search for broken links and to check technical aspects of websites and also tools to compare a site page with any number of other pages for all key SEO aspects and text to assess why it ranks at a certain position for a certain key phrase. It does not connect to or scan data from Google. I developed these tools for our own internal use and added them into PFCLScan.

In the last year I have helped 2 or 3 local customers with their own websites and the contents and ranking. I have invested years in web promotion for my own business and it is real world that works for me. I am not a web developer or SEO consultant BUT I have gained a lot of knowledge in this space so I am happy to help a small number of companies locally that i know personally and pass on that knowledge. I cannot say who they are as that would not be right but for two customers in this space I got then from position 7 and 6 to position 1 and they have remained there after more than 6 months as I use common sense and not tricks.

That said, my main focus is of course Oracle security and our software products focused on Oracle security.

Thanks to everyone for being part of my 23 year journey so far

#oracleace #sym_42 #oracle #security #lockdown #databreach