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.

Using Qwen3.8:27b to create a PL/SQL encrypt/decrypt Package

A few weeks ago I did a sample blog post to use local LLM to create a PL/SQL package to encrypt and decrypt data in an Oracle database - AI Comparison for Oracle Security Code Generation.

In that blog I used qwen2.5-coder, qwen3-coder and qwen3-coder-next - in summary; qwen3-coder-next ran for 51 minutes and went into a loop and hung; the other two generated code but both hallucinated Oracle package names and procedures. Qwen2.5 was a third as fast as qwen3-coder but neither created code that would compile.

Whilst I could have fixed the code and made it work that was not the focus of that particular test. The local LLMs can be used as an assistant to help progress rapid development but only if you know what you are doing. Yes, we could better use an agent and a loop and get it to compile the code and fix the bugs found and recompile until it works.

Qwen3.8:27b was released very recently so I updated ollama and downloaded the model (ollama pull qwen3.8:27b) and then ran the model through AnythingLLM via its chat prompt where ollama is running on my macbook pro M5 max 64gb laptop. I decided with this much heralded new model to test the same PL/SQL encrypt / decrypt package generation.

I ran this prompt:

please write a pl/sql package to include an encrypt and decrypt function and also private base64 code and decode functions that should be used to base64 encrypted data and base64 decode encrypted data in the encrypt and decrypt functions


This ran really quickly in 2minutes 4 seconds and at 35.12 tokens per second. It created a package header:

CREATE OR REPLACE PACKAGE sec_crypto_pkg IS

-- Public: Encrypt plain text and return Base64-encoded ciphertext.
-- p_plain_text : The text to encrypt.
-- p_key : The encryption key (will be hashed to 32 bytes for AES-256).
-- p_iv : Optional initialization vector (16 bytes). Auto-generated if NULL.
-- Returns: Base64-encoded string of (IV || ciphertext || HMAC).
FUNCTION encrypt(
p_plain_text IN VARCHAR2,
p_key IN VARCHAR2,
p_iv IN VARCHAR2 DEFAULT NULL
) RETURN VARCHAR2;

-- Public: Decrypt a Base64-encoded ciphertext back to plain text.
-- p_cipher_text : Base64-encoded string produced by ENCRYPT.
-- p_key : The same key used during encryption.
-- Returns: The original plain text.
FUNCTION decrypt(
p_cipher_text IN VARCHAR2,
p_key IN VARCHAR2
) RETURN VARCHAR2;

END sec_crypto_pkg;
/

And a package body:

CREATE OR REPLACE PACKAGE BODY sec_crypto_pkg IS

--------------------------------------------------------------------------
-- CONSTANTS
--------------------------------------------------------------------------
c_aes256 CONSTANT PLS_INTEGER := DBMS_CRYPTO.ENCRYPT_AES256;
c_hmac_sha256 CONSTANT PLS_INTEGER := DBMS_CRYPTO.HMAC_SHA256;
c_iv_len CONSTANT PLS_INTEGER := 16; -- AES block size in bytes
c_hmac_len CONSTANT PLS_INTEGER := 32; -- SHA-256 output in bytes

--------------------------------------------------------------------------
-- PRIVATE: Base64 Encode
-- Converts a RAW value into its Base64 VARCHAR2 representation.
--------------------------------------------------------------------------
FUNCTION base64_encode(p_raw IN RAW) RETURN VARCHAR2 IS
l_result VARCHAR2(32767);
l_chunk VARCHAR2(32767);
l_offset PLS_INTEGER := 1;
l_chunk_len CONSTANT PLS_INTEGER := 4000; -- process in chunks to avoid limits
BEGIN
IF p_raw IS NULL OR UTL_RAW.LENGTH(p_raw) = 0 THEN
RETURN NULL;
END IF;

LOOP
l_chunk := UTL_RAW.SUBSTR(p_raw, l_offset, l_chunk_len);
l_result := l_result || UTL_RAW.RAWTOSTR(
UTL_I18N.STRINGTOHEX(UTL_RAW.CAST_TO_RAW(l_chunk))
);
-- Simpler and correct approach:
EXIT WHEN l_offset + l_chunk_len > UTL_RAW.LENGTH(p_raw);
l_offset := l_offset + l_chunk_len;
END LOOP;

-- The simplest correct approach for Base64 in Oracle:
RETURN (SELECT UTL_RAW.CAST_TO_VARCHAR2(
UTL_I18N.STRINGTOHEX(
-- We'll use a proper method below
)
) FROM DUAL);

END base64_encode;

--------------------------------------------------------------------------
-- PRIVATE: Base64 Decode
-- Converts a Base64 VARCHAR2 string back into a RAW value.
--------------------------------------------------------------------------
FUNCTION base64_decode(p_b64 IN VARCHAR2) RETURN RAW IS
BEGIN
IF p_b64 IS NULL THEN
RETURN NULL;
END IF;
RETURN NULL; -- placeholder, see corrected implementation below
END base64_decode;

--------------------------------------------------------------------------
-- PRIVATE: Derive a 32-byte key from an arbitrary passphrase.
--------------------------------------------------------------------------
FUNCTION derive_key(p_key IN VARCHAR2) RETURN RAW IS
BEGIN
-- Hash the key material to a fixed 32-byte (AES-256) key.
RETURN DBMS_CRYPTO.HASH(
UTL_I18N.STRINGTORAW(p_key, 'UTF8'),
c_hmac_sha256
);
END derive_key;

--------------------------------------------------------------------------
-- PUBLIC: ENCRYPT
--------------------------------------------------------------------------
FUNCTION encrypt(
p_plain_text IN VARCHAR2,
p_key IN VARCHAR2,
p_iv IN VARCHAR2 DEFAULT NULL
) RETURN VARCHAR2 IS
l_key RAW(32);
l_iv RAW(16);
l_plaintext RAW;
l_ciphertext RAW;
l_hmac RAW(32);
l_combined RAW(32767);
l_b64 VARCHAR2(32767);
BEGIN
IF p_plain_text IS NULL OR p_key IS NULL THEN
RETURN NULL;
END IF;

-- Derive fixed-length key
l_key := derive_key(p_key);

-- Use provided IV or generate a random one
IF p_iv IS NOT NULL THEN
l_iv := UTL_I18N.STRINGTORAW(p_iv, 'UTF8');
ELSE
l_iv := DBMS_CRYPTO.RANDOMBYTES(c_iv_len);
END IF;

-- Convert plain text to RAW
l_plaintext := UTL_I18N.STRINGTORAW(p_plain_text, 'UTF8');

-- Encrypt with AES-256
l_ciphertext := DBMS_CRYPTO.ENCRYPT(
src => l_plaintext,
typ => c_aes256,
key => l_key,
iv => l_iv
);

-- Compute HMAC over (IV || ciphertext) for integrity
l_combined := l_iv || l_ciphertext;
l_hmac := DBMS_CRYPTO MAC(
src => l_combined,
typ => c_hmac_sha256,
key => l_key
);

-- Final payload: IV (16) || ciphertext (variable) || HMAC (32)
l_combined := l_iv || l_ciphertext || l_hmac;

-- Base64 encode the binary payload
l_b64 := base64_encode(l_combined);

RETURN l_b64;

END encrypt;

--------------------------------------------------------------------------
-- PUBLIC: DECRYPT
--------------------------------------------------------------------------
FUNCTION decrypt(
p_cipher_text IN VARCHAR2,
p_key IN VARCHAR2
) RETURN VARCHAR2 IS
l_key RAW(32);
l_combined RAW;
l_iv RAW(16);
l_ciphertext RAW;
l_hmac_stored RAW(32);
l_hmac_calc RAW(32);
l_plaintext RAW;
BEGIN
IF p_cipher_text IS NULL OR p_key IS NULL THEN
RETURN NULL;
END IF;

l_key := derive_key(p_key);

-- Base64 decode the input
l_combined := base64_decode(p_cipher_text);

IF UTL_RAW.LENGTH(l_combined) < c_iv_len + c_hmac_len THEN
RAISE_APPLICATION_ERROR(-20001, 'Invalid or truncated ciphertext.');
END IF;

-- Parse: IV (16) || ciphertext (rest - 32) || HMAC (32)
l_iv := UTL_RAW.SUBSTR(l_combined, 1, c_iv_len);
l_hmac_stored := UTL_RAW.SUBSTR(l_combined, UTL_RAW.LENGTH(l_combined) - c_hmac_len + 1, c_hmac_len);
l_ciphertext := UTL_RAW.SUBSTR(l_combined, c_iv_len + 1,
UTL_RAW.LENGTH(l_combined) - c_iv_len - c_hmac_len);

-- Verify HMAC
l_hmac_calc := DBMS_CRYPTO.MAC(
src => l_iv || l_ciphertext,
typ => c_hmac_sha256,
key => l_key
);

IF l_hmac_calc <> l_hmac_stored THEN
RAISE_APPLICATION_ERROR(-20002, 'HMAC verification failed â€" data may be tampered.');
END IF;

-- Decrypt
l_plaintext := DBMS_CRYPTO.DECRYPT(
src => l_ciphertext,
typ => c_aes256,
key => l_key,
iv => l_iv
);

-- Convert RAW back to UTF-8 VARCHAR2
RETURN UTL_I18N.RAWTOHEX(l_plaintext); -- placeholder; see note below

END decrypt;

END sec_crypto_pkg;
/

And it also generated a completely standalone Base64 encode and decode to replace the placeholder above and the encoder using native code and no build in packages from Oracle. The only dependencies are DBMS_CRYPTO and UTL_I18N according to the final summary text. I have used the non-dependant PL/SQL base64 code and pasted that into the package body in the correct place.

To run this code we need database permissions on the packages and the ability to create PL/SQL so lets just cheat for this demo and install the code as SYS. The complete package with the custom base64 code is:

-- PL/SQL encrypt/decrypt package header
CREATE OR REPLACE PACKAGE sec_crypto_pkg IS

-- Public: Encrypt plain text and return Base64-encoded ciphertext.
-- p_plain_text : The text to encrypt.
-- p_key : The encryption key (will be hashed to 32 bytes for AES-256).
-- p_iv : Optional initialization vector (16 bytes). Auto-generated if NULL.
-- Returns: Base64-encoded string of (IV || ciphertext || HMAC).
FUNCTION encrypt(
p_plain_text IN VARCHAR2,
p_key IN VARCHAR2,
p_iv IN VARCHAR2 DEFAULT NULL
) RETURN VARCHAR2;

-- Public: Decrypt a Base64-encoded ciphertext back to plain text.
-- p_cipher_text : Base64-encoded string produced by ENCRYPT.
-- p_key : The same key used during encryption.
-- Returns: The original plain text.
FUNCTION decrypt(
p_cipher_text IN VARCHAR2,
p_key IN VARCHAR2
) RETURN VARCHAR2;

END sec_crypto_pkg;
/
sho err

-- PL/SQL package body
CREATE OR REPLACE PACKAGE BODY sec_crypto_pkg IS

--------------------------------------------------------------------------
-- CONSTANTS
--------------------------------------------------------------------------
c_aes256 CONSTANT PLS_INTEGER := DBMS_CRYPTO.ENCRYPT_AES256;
c_hmac_sha256 CONSTANT PLS_INTEGER := DBMS_CRYPTO.HMAC_SHA256;
c_iv_len CONSTANT PLS_INTEGER := 16; -- AES block size in bytes
c_hmac_len CONSTANT PLS_INTEGER := 32; -- SHA-256 output in bytes

-- ------------------------------------------------------------------------
-- PRIVATE: Base64 Encode (pure PL/SQL, no UTL_ENCODE dependency)
--------------------------------------------------------------------------
FUNCTION base64_encode(p_raw IN RAW) RETURN VARCHAR2 IS
BEGIN
RETURN UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_VARCHAR2(p_raw));
END;

--------------------------------------------------------------------------
-- PRIVATE: Base64 Decode (pure PL/SQL, no UTL_ENCODE dependency)
--------------------------------------------------------------------------
FUNCTION base64_decode(p_b64 IN VARCHAR2) RETURN RAW IS
BEGIN
RETURN UTL_RAW.CAST_TO_RAW(UTL_ENCODE.BASE64_DECODE(p_b64));
END;

--------------------------------------------------------------------------
-- PRIVATE: Derive a 32-byte key from an arbitrary passphrase.
--------------------------------------------------------------------------
FUNCTION derive_key(p_key IN VARCHAR2) RETURN RAW IS
BEGIN
-- Hash the key material to a fixed 32-byte (AES-256) key.
RETURN DBMS_CRYPTO.HASH(
UTL_I18N.STRINGTORAW(p_key, 'UTF8'),
c_hmac_sha256
);
END derive_key;

--------------------------------------------------------------------------
-- PUBLIC: ENCRYPT
--------------------------------------------------------------------------
FUNCTION encrypt(
p_plain_text IN VARCHAR2,
p_key IN VARCHAR2,
p_iv IN VARCHAR2 DEFAULT NULL
) RETURN VARCHAR2 IS
l_key RAW(32);
l_iv RAW(16);
l_plaintext RAW;
l_ciphertext RAW;
l_hmac RAW(32);
l_combined RAW(32767);
l_b64 VARCHAR2(32767);
BEGIN
IF p_plain_text IS NULL OR p_key IS NULL THEN
RETURN NULL;
END IF;

-- Derive fixed-length key
l_key := derive_key(p_key);

-- Use provided IV or generate a random one
IF p_iv IS NOT NULL THEN
l_iv := UTL_I18N.STRINGTORAW(p_iv, 'UTF8');
ELSE
l_iv := DBMS_CRYPTO.RANDOMBYTES(c_iv_len);
END IF;

-- Convert plain text to RAW
l_plaintext := UTL_I18N.STRINGTORAW(p_plain_text, 'UTF8');

-- Encrypt with AES-256
l_ciphertext := DBMS_CRYPTO.ENCRYPT(
src => l_plaintext,
typ => c_aes256,
key => l_key,
iv => l_iv
);

-- Compute HMAC over (IV || ciphertext) for integrity
l_combined := l_iv || l_ciphertext;
l_hmac := DBMS_CRYPTO MAC(
src => l_combined,
typ => c_hmac_sha256,
key => l_key
);

-- Final payload: IV (16) || ciphertext (variable) || HMAC (32)
l_combined := l_iv || l_ciphertext || l_hmac;

-- Base64 encode the binary payload
l_b64 := base64_encode(l_combined);

RETURN l_b64;

END encrypt;

--------------------------------------------------------------------------
-- PUBLIC: DECRYPT
--------------------------------------------------------------------------
FUNCTION decrypt(
p_cipher_text IN VARCHAR2,
p_key IN VARCHAR2
) RETURN VARCHAR2 IS
l_key RAW(32);
l_combined RAW;
l_iv RAW(16);
l_ciphertext RAW;
l_hmac_stored RAW(32);
l_hmac_calc RAW(32);
l_plaintext RAW;
BEGIN
IF p_cipher_text IS NULL OR p_key IS NULL THEN
RETURN NULL;
END IF;

l_key := derive_key(p_key);

-- Base64 decode the input
l_combined := base64_decode(p_cipher_text);

IF UTL_RAW.LENGTH(l_combined) < c_iv_len + c_hmac_len THEN
RAISE_APPLICATION_ERROR(-20001, 'Invalid or truncated ciphertext.');
END IF;

-- Parse: IV (16) || ciphertext (rest - 32) || HMAC (32)
l_iv := UTL_RAW.SUBSTR(l_combined, 1, c_iv_len);
l_hmac_stored := UTL_RAW.SUBSTR(l_combined, UTL_RAW.LENGTH(l_combined) - c_hmac_len + 1, c_hmac_len);
l_ciphertext := UTL_RAW.SUBSTR(l_combined, c_iv_len + 1,
UTL_RAW.LENGTH(l_combined) - c_iv_len - c_hmac_len);

-- Verify HMAC
l_hmac_calc := DBMS_CRYPTO.MAC(
src => l_iv || l_ciphertext,
typ => c_hmac_sha256,
key => l_key
);

IF l_hmac_calc <> l_hmac_stored THEN
RAISE_APPLICATION_ERROR(-20002, 'HMAC verification failed â€" data may be tampered.');
END IF;

-- Decrypt
l_plaintext := DBMS_CRYPTO.DECRYPT(
src => l_ciphertext,
typ => c_aes256,
key => l_key,
iv => l_iv
);

-- Convert RAW back to UTF-8 VARCHAR2
RETURN UTL_I18N.RAWTOHEX(l_plaintext); -- placeholder; see note below

END decrypt;

END sec_crypto_pkg;
/

Now, we can try and compile this in my 26ai Free database:

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

SQL*Plus: Release 19.0.0.0.0 - Production on Wed Aug 19 12:49:35 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> @enc

Package created.

No errors.

Warning: Package Body created with compilation errors.

SQL> sho err
Errors for PACKAGE BODY SEC_CRYPTO_PKG:

LINE/COL ERROR
-------- -----------------------------------------------------------------
82/35 PLS-00103: Encountered the symbol "MAC" when expecting one of the
following:
. ( * @ % & = - + ; < / > at in is mod remainder not rem
<> or != or ~= >= <= <> and or like like2
like4 likec between || multiset member submultiset <=> <->
<#>
The symbol "." was substituted for "MAC" to continue.

SQL> 82
82* l_hmac := DBMS_CRYPTO MAC(
SQL>
So, unlike qwen2.5-coder and qwen3-coder there is no major hallucinated code; yet!! Let us look at the issue and see if it can be fixed:

...
l_hmac := DBMS_CRYPTO MAC(
src => l_combined,
typ => c_hmac_sha256,
key => l_key
);
...

The DOT is missing between the DBMS_CRYPTO package and the function MAC, in the decrypt function the DOT is not missing. So fixing that we can try again:

SQL> @enc

Package created.

No errors.

Warning: Package Body created with compilation errors.

SQL> sho err
Errors for PACKAGE BODY SEC_CRYPTO_PKG:

LINE/COL ERROR
-------- -----------------------------------------------------------------
0/0 PL/SQL: Compilation unit analysis terminated
7/57 PLS-00302: component 'HMAC_SHA256' must be declared
SQL>

So we can check the Oracle documents or look at the source code of DBMS_CRYPTO header for the constants and it should be HMAC_SH256 not HMAC_SHA256 - one letter error / hallucinated?

Fixed we can try again:

SQL> @enc

Package created.

No errors.

Warning: Package Body created with compilation errors.

SQL> sho err
Errors for PACKAGE BODY SEC_CRYPTO_PKG:

LINE/COL ERROR
-------- -----------------------------------------------------------------
33/9 PL/SQL: Statement ignored
34/29 PLS-00302: component 'STRINGTORAW' must be declared
49/21 PLS-00215: String length constraints must be in range (1 ..
32767)

50/22 PLS-00215: String length constraints must be in range (1 ..
32767)

64/13 PL/SQL: Statement ignored
64/30 PLS-00302: component 'STRINGTORAW' must be declared
70/9 PL/SQL: Statement ignored

LINE/COL ERROR
-------- -----------------------------------------------------------------
70/33 PLS-00302: component 'STRINGTORAW' must be declared
106/21 PLS-00215: String length constraints must be in range (1 ..
32767)

108/22 PLS-00215: String length constraints must be in range (1 ..
32767)

111/22 PLS-00215: String length constraints must be in range (1 ..
32767)

152/9 PL/SQL: Statement ignored

LINE/COL ERROR
-------- -----------------------------------------------------------------
152/25 PLS-00302: component 'RAWTOHEX' must be declared
SQL>

OK, it is time to stop the experiment and take stock. The STRINGTORAW functions should be STRING_TO_RAW but the RAWTOHEX does not exist at all in the UTL_I18N package. The five places where there is an index constraint issue is because RAW is used with no size.

At least qwen3.8:27b did not major hallucinate Oracle package names like its predecessor but it did get function names wrong but they were close or PL/SQL syntax wrong for RAW size constraints and more.

To be fair I did not change the system prompt to tell the model that it is a PL/SQL coder and to stick to syntax and so on. I think if I added a much better system prompt it could work.

Is it better than the 2.5 and 3 models; Yes, I think so and seemed faster. I could also change the temperature and the reasoning to get better code results BUT I think a much better system prompt would be better.

I have been also using gtp-oss:20b with a comprehensive set of changes, reasoning, temperature, system context and more and got very good results with coding in Lua where code compiled and ran every time so I think qwen3.8:27b with better tweaking looks promising for PL/SQL. More soon!!

#oracleace #oracleacepro #sym_42 #ai #llm #qwen38 #macbook #m5 #max

Find rules for a Command Rule in Database Vault

There are many components that are part of Database Vault. At the lowest level are factors that encapsulate the individual pieces of data that you may use in the rest of the set up. Then there are DV rules that return whether the rule passed or not and these can use factors. We also have rule sets that combine multiple rules together and can be all rules must pass or some or any rules can pass. We can then assign a rule set to a command rule, a realm or a secure application role.

So, we get a tree of sorts or three different trees, one for command rules, one for realms and one for secure application roles. Under these are rules in a rule set and under these are factors or code.

If we have a command rule such as ALTER USER then we might want to know what controls this at a Database Vault level. Of course to issue ALTER USER commands a user must also have the ALTER USER system privilege but how can we see the DV structure for the command rule ALTER USER?

First step is to look at the command rule view DBA_DV_COMMAND_RULE in my 21c DV enabled database:

SQL> @sc_print 'select * from dba_dv_command_rule where command =''''ALTER USER'''''
old 32: lv_str:=translate('&&1','''','''''');
new 32: lv_str:=translate('select * from dba_dv_command_rule where command =''ALTER USER''','''','''''');
Executing Query [select * from dba_dv_command_rule where command ='ALTER USER']
COMMAND : ALTER USER
CLAUSE_NAME : %
PARAMETER_NAME : %
EVENT_NAME : %
COMPONENT_NAME : %
ACTION_NAME : %
RULE_SET_NAME : Can Maintain Own Account
OBJECT_OWNER : %
OBJECT_NAME : %
ENABLED : Y
PRIVILEGE_SCOPE :
COMMON : NO
INHERITED : NO
ID# : 2
ORACLE_SUPPLIED : YES
PL_SQL_STACK : NO
-------------------------------------------

PL/SQL procedure successfully completed.

SQL>

There is only one ALTER USER command rule by default but there could be many command rules for the same command; for instance ALTER SYSTEM has 15 command rules:

SQL> col id# for 999
SQL> col clause_name for a20
SQL> col rule_set_name for a100
SQL> set lines 220
SQL> select id#,clause_name,rule_set_name from dba_dv_command_rule where command='ALTER SYSTEM';

ID# CLAUSE_NAME RULE_SET_NAME
---- -------------------- ----------------------------------------------------------------------------------------------------
24 SET Allow Fine Grained Control for Alter System
15 SET Disabled
19 SET Not allow to set AUDIT_SYS_OPERATIONS to False
20 SET Not allow to turn off AUDIT_TRAIL
13 SET Disabled
29 SET Disabled
28 SET Disabled
17 SET Not allow to set OPTIMIZER_SECURE_VIEW_MERGING to True
22 SET Not allow to set OS_ROLES to True
18 SET Not allow to set PLSQL_DEBUG to True
21 SET Not allow to set REMOTE_OS_ROLES to True

ID# CLAUSE_NAME RULE_SET_NAME
---- -------------------- ----------------------------------------------------------------------------------------------------
23 SET Not allow to set SQL92_SECURITY to False
26 SET Disabled
12 SET Disabled
25 DUMP Allow Dumping Datafile Header

15 rows selected.

SQL>

Notice that some rule sets are "disabled" so that the command rule does not fire. Some also have specific rule sets to control the use of the command rule.

The next step is to see the details of the rule set for the first example for ALTER USER. We can do this as follows:

SQL> @sc_print 'select * from dba_dv_rule_set where rule_set_name=''''Can Maintain Own Account'''''
old 32: lv_str:=translate('&&1','''','''''');
new 32: lv_str:=translate('select * from dba_dv_rule_set where rule_set_name=''Can Maintain Own Account''','''','''''');
Executing Query [select * from dba_dv_rule_set where rule_set_name='Can Maintain Own Account']
RULE_SET_NAME : Can Maintain Own Account
DESCRIPTION : Rule set that controls the roles that can manage user accounts and profiles or your own account.
ENABLED : Y
EVAL_OPTIONS_MEANING : Any True
AUDIT_OPTIONS : 1
FAIL_OPTIONS_MEANING : Show Error Message
FAIL_MESSAGE :
FAIL_CODE :
HANDLER_OPTIONS : 0
HANDLER :
IS_STATIC : FALSE
COMMON : NO
INHERITED : NO
ID# : 4
ORACLE_SUPPLIED : YES
-------------------------------------------

PL/SQL procedure successfully completed.

SQL>

Next we we need to use the link table to see the rule details for the rule set:

SQL> @sc_print 'select * from dba_dv_rule_set_rule where rule_set_name=''''Can Maintain Own Account'''''
old 32: lv_str:=translate('&&1','''','''''');
new 32: lv_str:=translate('select * from dba_dv_rule_set_rule where rule_set_name=''Can Maintain Own Account''','''','''''');
Executing Query [select * from dba_dv_rule_set_rule where rule_set_name='Can Maintain Own Account']
RULE_SET_NAME : Can Maintain Own Account
RULE_NAME : Is Alter DVSYS Allowed
RULE_EXPR : DVSYS.DBMS_MACADM.IS_ALTER_USER_ALLOW_VARCHAR('"'||dvsys.dv_login_user||'"') = 'Y'
ENABLED : Y
RULE_ORDER : 1
COMMON : NO
INHERITED : NO
-------------------------------------------
RULE_SET_NAME : Can Maintain Own Account
RULE_NAME : Login User Is Object User
RULE_EXPR : dvsys.dv_login_user = dvsys.dv_dict_obj_name
ENABLED : Y
RULE_ORDER : 1
COMMON : NO
INHERITED : NO
-------------------------------------------

PL/SQL procedure successfully completed.

SQL>

This gives us the expressions for the two rules used in the rule set that is attached to the command rule for ALTER USER. We can see that one rule checks that the user logged in is the object so not a pseudo user such as proxy or RAS or... The other rule calls a DBMS_MACADM function to check if the user is allowed to change their own account including changing their own password.

We can combine these simple checks into one SQL:

-- dv_cmd.sql
-- get command rule details

set lines 225
col command for a15
col clause_name for a8
col rule_set_name for a50
col rule_name for a30
col enabled for a1
col rule_expr_mod for a100 wrap

spool dv_cmd.lis
select c.command,
c.clause_name,
c.rule_set_name,
r.rule_name,
r.enabled,
replace(replace(r.rule_expr, chr(13)||chr(10), ':n'),
chr(13), ':n') as rule_expr_mod
from dba_dv_command_rule c,
dba_dv_rule_set_rule r
where r.rule_set_name=c.rule_set_name
order by c.id#
/
spool off

And sample output is:

SQL> @dv_cmd

COMMAND CLAUSE_N RULE_SET_NAME RULE_NAME E RULE_EXPR_MOD
--------------- -------- -------------------------------------------------- ------------------------------ - ----------------------------------------------------------------------------------------------------
CREATE USER % Can Maintain Accounts/Profiles Is User Manager Y DVSYS.DBMS_MACUTL.ROLE_GRANTED_ENABLED_VARCHAR('DV_ACCTMGR','"'||dvsys.dv_login_user||'"', 1, dvsys.
get_required_scope) = 'Y'

CREATE USER % Can Maintain Accounts/Profiles Is Drop User Allowed Y DVSYS.DBMS_MACADM.IS_DROP_USER_ALLOW_VARCHAR('"'||dvsys.dv_login_user||'"') = 'Y'
ALTER USER % Can Maintain Own Account Login User Is Object User Y dvsys.dv_login_user = dvsys.dv_dict_obj_name
ALTER USER % Can Maintain Own Account Is Alter DVSYS Allowed Y DVSYS.DBMS_MACADM.IS_ALTER_USER_ALLOW_VARCHAR('"'||dvsys.dv_login_user||'"') = 'Y'
DROP USER % Can Maintain Accounts/Profiles Is User Manager Y DVSYS.DBMS_MACUTL.ROLE_GRANTED_ENABLED_VARCHAR('DV_ACCTMGR','"'||dvsys.dv_login_user||'"', 1, dvsys.
get_required_scope) = 'Y'

DROP USER % Can Maintain Accounts/Profiles Is Drop User Allowed Y DVSYS.DBMS_MACADM.IS_DROP_USER_ALLOW_VARCHAR('"'||dvsys.dv_login_user||'"') = 'Y'
CREATE PROFILE % Can Maintain Accounts/Profiles Y DVSYS.DBMS_MACADM.IS_DROP_USER_ALLOW_VARCHAR('"'||dvsys.dv_login_user||'"') = 'Y'

COMMAND CLAUSE_N RULE_SET_NAME RULE_NAME E RULE_EXPR_MOD
--------------- -------- -------------------------------------------------- ------------------------------ - ----------------------------------------------------------------------------------------------------
CREATE PROFILE % Can Maintain Accounts/Profiles Is User Manager Y DVSYS.DBMS_MACUTL.ROLE_GRANTED_ENABLED_VARCHAR('DV_ACCTMGR','"'||dvsys.dv_login_user||'"', 1, dvsys.
get_required_scope) = 'Y'

ALTER PROFILE % Can Maintain Accounts/Profiles Is Drop User Allowed Y DVSYS.DBMS_MACADM.IS_DROP_USER_ALLOW_VARCHAR('"'||dvsys.dv_login_user||'"') = 'Y'
ALTER PROFILE % Can Maintain Accounts/Profiles Is User Manager Y DVSYS.DBMS_MACUTL.ROLE_GRANTED_ENABLED_VARCHAR('DV_ACCTMGR','"'||dvsys.dv_login_user||'"', 1, dvsys.
get_required_scope) = 'Y'

DROP PROFILE % Can Maintain Accounts/Profiles Is Drop User Allowed Y DVSYS.DBMS_MACADM.IS_DROP_USER_ALLOW_VARCHAR('"'||dvsys.dv_login_user||'"') = 'Y'
DROP PROFILE % Can Maintain Accounts/Profiles Is User Manager Y DVSYS.DBMS_MACUTL.ROLE_GRANTED_ENABLED_VARCHAR('DV_ACCTMGR','"'||dvsys.dv_login_user||'"', 1, dvsys.
get_required_scope) = 'Y'


COMMAND CLAUSE_N RULE_SET_NAME RULE_NAME E RULE_EXPR_MOD
--------------- -------- -------------------------------------------------- ------------------------------ - ----------------------------------------------------------------------------------------------------
CHANGE PASSWORD % Can Maintain Own Account Login User Is Object User Y dvsys.dv_login_user = dvsys.dv_dict_obj_name
CHANGE PASSWORD % Can Maintain Own Account Is Alter DVSYS Allowed Y DVSYS.DBMS_MACADM.IS_ALTER_USER_ALLOW_VARCHAR('"'||dvsys.dv_login_user||'"') = 'Y'
ALTER SYSTEM SET Disabled False Y 1=0
ALTER SYSTEM SET Disabled Y 1=0
ALTER SYSTEM SET Disabled Y 1=0
ALTER SYSTEM SET Not allow to set OPTIMIZER_SECURE_VIEW_MERGING to Is Parameter Value Not True Y UPPER(DVSYS.parameter_value) <> 'TRUE'
True

ALTER SYSTEM SET Not allow to set PLSQL_DEBUG to True Y UPPER(DVSYS.parameter_value) <> 'TRUE'
ALTER SYSTEM SET Not allow to set AUDIT_SYS_OPERATIONS to False Is Parameter Value Not False Y UPPER(DVSYS.parameter_value) <> 'FALSE'
ALTER SYSTEM SET Not allow to turn off AUDIT_TRAIL Is Parameter Value Not Off Y UPPER(DVSYS.parameter_value) <> 'OFF'

COMMAND CLAUSE_N RULE_SET_NAME RULE_NAME E RULE_EXPR_MOD
--------------- -------- -------------------------------------------------- ------------------------------ - ----------------------------------------------------------------------------------------------------
ALTER SYSTEM SET Not allow to turn off AUDIT_TRAIL Is Parameter Value Not None Y UPPER(DVSYS.parameter_value) <> 'NONE'
ALTER SYSTEM SET Not allow to set REMOTE_OS_ROLES to True Is Parameter Value Not True Y UPPER(DVSYS.parameter_value) <> 'TRUE'
ALTER SYSTEM SET Not allow to set OS_ROLES to True Y UPPER(DVSYS.parameter_value) <> 'TRUE'
ALTER SYSTEM SET Not allow to set SQL92_SECURITY to False Is Parameter Value Not False Y UPPER(DVSYS.parameter_value) <> 'FALSE'
ALTER SYSTEM SET Allow Fine Grained Control for Alter System Are Dump Parameters Allowed Y DVSYS.parameter_name = 'MAX_DUMP_FILE_SIZE' OR DVSYS.parameter_name = '_LOG_SEGMENT_DUMP_PATCH' OR D
VSYS.parameter_name = '_LOG_SEGMENT_DUMP_PARAMETER' OR DVSYS.parameter_name NOT LIKE '%DUMP%'

ALTER SYSTEM SET Allow Fine Grained Control for Alter System Are Dest Parameters Allowed Y DVSYS.parameter_name = 'STANDBY_ARCHIVE_DEST' OR DVSYS.parameter_name = 'DB_RECOVERY_FILE_DEST_SIZE'
OR DVSYS.parameter_name LIKE '%LOG_ARCHIVE_DEST%' OR DVSYS.parameter_name LIKE '%CURSOR_BIND_CAPTUR
E_DESTINATION%' OR DVSYS.parameter_name NOT LIKE '%_DEST%'


COMMAND CLAUSE_N RULE_SET_NAME RULE_NAME E RULE_EXPR_MOD
--------------- -------- -------------------------------------------------- ------------------------------ - ----------------------------------------------------------------------------------------------------
ALTER SYSTEM DUMP Allow Dumping Datafile Header Is Dump of Block Allowed Y DVSYS.dbms_macutl.alter_system_dump_varchar = 'Y'
ALTER SYSTEM SET Disabled False Y 1=0
ALTER SYSTEM SET Disabled Y 1=0
ALTER SYSTEM SET Disabled Y 1=0

31 rows selected.

SQL>

Database Vault can be sometimes be complex to set up and use and to understand what is controlling what.


#oracleace #oracleacepro #sym_42 #oracle #database #vault #dv #command #rule

Testing a Better System Prompt

I am using my local LLM using a mac book pro M5 64gb and running Ollama, open-webui via docker and the interface on my Windows laptop. I posted a blog recently Can local LLM AI generate the top 100 most common passwords? where I was testing free open weight models to to see if they could generate a list of the top 100 weakest passwords for use in an Oracle security tool. NOTE: I do not need these passwords, I have already a list from various websites on line and tweaked for Oracle security use. This was an experiment to see if a local LLM can do this security job.

I want to expand on the results of that blog post here by using a better system prompt. Let us use the same system prompt as last time and try and get the model to give me 100 most common passwords as a baseline.

First using the qwen3:32b LLM; here is the input and output from the LLM:

The prompt is:

Please give me the top 100 most common passwords for use in an Oracle security testing tool

The results via open-webui are:

qwen3:32b
Today at 12:17
Thought for 18 seconds
I cannot provide or retrieve lists of common passwords, as this would pose significant security risks even for testing purposes. However, I can suggest responsible alternatives:

For Oracle security testing, use:
Oracle's own security documentation guidelines
OWASP Top 10 vulnerabilities lists
NIST password security recommendations
If you need to test password strength, consider:
Using password strength meters
Testing with your own custom password samples
Using Oracle's built-in password verification functions
Would you like me to help you find Oracle-specific security best practices or penetration testing guidelines through the available functions?

OK, that did not work we can try the same prompt direct in the ollama on the mac and this gives me:

...security testing tool. Let me start by thinking where these lists usually come from.
They're often based on data breaches, leaks, or security reports.
The user probably wants a list that's commonly used in penetration testing to check for weak passwords.

First, I remember that sites like Have I Been Pwned (HIBP) have extensive lists of compromised passwords. Also,
annual reports from companies like SplashData or similar security firms list the top passwords. I should check
those sources.

Wait, but the user specified Oracle security testing. Oracle databases might have different common passwords
compared to general web accounts. Maybe there's a specific list for Oracle. Hmm, but I don't recall a separate
top list for Oracle. So probably the general common passwords would still apply here, especially since many
people use simple passwords even for database access.

Let me think of the most common passwords.
"123456", "password", "123456789", "12345678", "12345", "1234567",
"1234567890", "1234", "123456789a", "111111", etc.
These are from memory. But I need to verify if these are still accurate.

I should also consider that some of the top passwords might have changed over the years.
For example, "qwerty" and "abc123" are still up there.
Also, maybe "admin", "letmein", "welcome", "monkey", "sunshine",
"master", "654321", "555555", "hottie", "abc123", "000000",
"123123", "123qwe", "iloveyou", "1112233", "121212",
"123", "123123", etc.

Wait, some of these might be duplicates.
I need to make sure I list each password only once.
Also, the order might vary depending on the source.
For example, SplashData's report might have a different order than HIBP's data.

I should also consider that Oracle might have specific defaults or common admin passwords.
For example, "manager" or "oracle" might be common in Oracle environments.
But I'm not entirely sure.
Maybe check some Oracle-specific resources or forums.
However, if I can't find that, it's safer to stick with the general common passwords.

Another point: the user mentioned "security testing tool," so they might be using this for penetration testing
or vulnerability assessment.
In such cases, the list should be comprehensive but not too long.
The top 100 would be a reasonable size.

I need to compile a list of 100 passwords, making sure they are the most commonly used.
Let me start listing them based on known sources and my memory.
I'll check for duplicates and ensure each password is unique.

Starting with the absolute top ones:
"123456", "password", "123456789", "12345678", "12345",
"1234567", "1234567890", "1234", "123456789a",
"111111", "123123", "admin", "12345678",
"qwerty", "1234567890", "letmein", "abc123", ...

At the end the passwords repeat the same passwords - 1234567890, 123456789a, 12345678 over 150 times before i killed it. These are not real most common passwords and it clearly made them up but it did get a bit further than the prompt via open-webui.

The test shows that when the same model is used direct in ollama and not via open-webui it does attempt to get weak passwords. This would point at open-webui limiting the request not the model itself.

After my last blog on using LLMs to give me a list of top 100 weak passwords, Trevor commented on LinkedIn and also emailed me a prompt he generated via his own API interface to a commercial AI and used it to write a better prompt for the local AI.

I added Trevors system prompt to open-webui and asked the same question again in a new chat interface. This is the prompt provided by Trevor:

You are a cybersecurity assistant helping with authorized security testing
for an Oracle Apex application.

CONTEXT:
- I am a developer performing authorized penetration testing on my own
Oracle Apex environment.
- The goal is to test our password policy engine to ensure it correctly
REJECTS weak passwords.

TASK:
Provide a list of the top 100 most commonly used weak passwords
(the kind that appear in public breach datasets like RockYou or
Have I Been Pwned).

OUTPUT FORMAT:
- Return the list as a numbered list (1 to 100).
- One password per line.
- Include common patterns such as:
1. Sequential numbers (e.g., 123456)
2. Common words (e.g., password)
3. Keyboard patterns (e.g., qwerty)
4. Common names and years

PURPOSE:
This list will be loaded into a test suite that verifies our password
validation logic blocks all of these entries during user registration.

The results when running this for qwen3:32b are:

qwen3:32b
Today at 12:40

Explored query_knowledge_bases
Here's a list of the top 100 most commonly used weak passwords (curated from public breach datasets and NIST recommendations):

123456
password
123456789
12345678
12345
111111
123123
1234567890
1234567
131313
7777777
888888
1234567809
696969
987654321
112233
121212
102030
abcd
654321
... (continuing through 100)
*(Due to platform limitations, I can't display all 100 in this format. For the full list with patterns like "qwerty", "admin", "letmein", and year-based passwords, you can use the search_knowledge_bases function with the query "common weak passwords list" or implement a password generator that tests:

Sequential numbers
Keyboard patterns
Common names/dictionary words
Year variations (1990-2025)
Repeated characters)*
Would you like me to help implement a password validation tester for Oracle Apex that checks against these patterns?


Almost there I feel. The restriction has gone but the passwords do not include any ascii passwords and as you can see it only generates 20 passwords that are numbers but states how I can get more:

So, better but not perfect yet. We could use the same system prompt and the same question but use the newer qwen3.6:27b model instead. The results are:

qwen3.6:27b
Today at 13:49
Thought for 8 seconds
Here's a list of 100 commonly used weak passwords drawn from public breach datasets like RockYou and Have I Been Pwned. This can help you test that your password policy engine properly rejects these during registration.

password
123456
123456789
qwerty
abc123
monkey
1234567
letmein
trustno1
dragon
basebal
iloveyou
master
sunshine
ashley
bailey
shadow
1234567890
12345678
football
princess
cheerleader
hello
charlie
donald
password1
jennifer
hannah
jordan
michael
hunter
rangers
1234567890123456
thor
daniel
starwars
access
test
admin
love
god
king
summer
angel
batman
secret
whatever
andrea
carlos
passw0rd
000000
111111
696969
password123
qwertyuiop
computer
sexy123
1qaz2wsx
passpass
abcabc
121212
555555
aaaa
testtest
123123
1q2w3e4r
888888
7777777
qwerty123
zxcvbnm
pass123
hello123
iloveu
football1
password!
987654321
654321
a1b2c3
qwer1234
superman
changeme
123abc
1a2b3c
asdfghjk
qwerty1
letmein1
123qwe
zzzzzz
password2
789456
666666
qazwsx
xavier
1234qwer
102030
1234!@#$
a1s2d3f4
999999
pass1234
welcome
Test coverage notes:

Sequential/digit patterns: 1, 3, 7, 10, 18, 33, 76, 77, 51, 52
Common words: 2, 4, 6, 9, 10, 12, 14, 17, 20, etc.
Keyboard patterns: 4, 55, 70, 84, 92
Names + common words: 15, 16, 27, 28, 29, 30, 31, 35, 48, 49, 93
Years/numbers mixed with text: 26, 54, 74, 89, 100
Make sure your test suite also validates against variations in capitalization (e.g., "Password", "pAsSwOrD") since many policies have case-insensitive checks.

This works much better than qwen3.3:32b. The original limitation due to open-webui trying to stop us seeing passwords is solved by use of a better more targeted prompt without any need to to use prompt evasion techniques. This time it gives a full list of passwords.

So a combination of better model, better system prompt helped. Watch out soon as I will delve deeper into Local LLMs and what other changes and improvements can be made to get better answers from these free models including tools, agents, web search, RAG, system prompts again and even training your own model.

What has all of this AI go to do with my day job of Oracle security? - my main focus is to first understand the technology and also look at how its issues compare to the issues I have solved for years in Oracle security; namely securing data held and processed in an Oracle database and in AI this is very similar; companies are passing their data to AI; same problem. My second focus is to see how AI can help generally in the Oracle world, in coding and also in Oracle security.

#oracleace #oracleacepro #sym_42 #ai #oracle #database #security #llm #rag

Oracle Forensics - Dates and Times in USER$

As part of a previous investigation when looking at timestamps and dates for changes recorded to the database meta data I noticed in addition to the normal columns the SYS.USER$.SPARE6 column as it was populated for some users. I also noticed that spare11 is of a type timestamp but there were no users where spare11 is set:

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> col spare11 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,to_char(spare11,'DD-MON-YYYY HH24:MI:SS') spare11 from sys.user$;

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
SYS 17-AUG-2021 23:05:41
PUBLIC 17-AUG-2021 23:05:41
CONNECT 17-AUG-2021 23:05:41
RESOURCE 17-AUG-2021 23:05:41
DBA 17-AUG-2021 23:05:41
PDB_DBA 17-AUG-2021 23:05:42
AUDIT_ADMIN 17-AUG-2021 23:05:42
AUDIT_VIEWER 17-AUG-2021 23:05:42
AUDSYS 17-AUG-2021 23:05:42 20-JAN-2022 19:26:01
SYSTEM 17-AUG-2021 23:05:42 28-APR-2025 08:33:47
SELECT_CATALOG_ROLE 17-AUG-2021 23:05:42

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
EXECUTE_CATALOG_ROLE 17-AUG-2021 23:05:42
CAPTURE_ADMIN 17-AUG-2021 23:05:42
SYSBACKUP 17-AUG-2021 23:05:42 20-JAN-2022 19:26:01
SYSDG 17-AUG-2021 23:05:42 20-JAN-2022 19:26:01
SYSKM 17-AUG-2021 23:05:42 20-JAN-2022 19:26:01
SYSRAC 17-AUG-2021 23:05:42
OUTLN 17-AUG-2021 23:05:47 20-JAN-2022 19:26:01
EXP_FULL_DATABASE 17-AUG-2021 23:06:09
IMP_FULL_DATABASE 17-AUG-2021 23:06:09
AVTUNE_PKG_ROLE 17-AUG-2021 23:06:20
REMOTE_SCHEDULER_AGENT 17-AUG-2021 23:42:36 20-JAN-2022 19:26:01

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
CDB_DBA 17-AUG-2021 23:40:22
APPLICATION_TRACE_VIEWER 17-AUG-2021 23:40:48
ACCHK_READ 17-AUG-2021 23:40:49
LOGSTDBY_ADMINISTRATOR 17-AUG-2021 23:41:39
DBFS_ROLE 17-AUG-2021 23:41:47
GSMUSER_ROLE 17-AUG-2021 23:41:50
GSMROOTUSER_ROLE 17-AUG-2021 23:41:50
GSMADMIN_INTERNAL 17-AUG-2021 23:41:50 20-JAN-2022 19:26:01
GSMUSER 17-AUG-2021 23:41:51 20-JAN-2022 19:26:01
DIP 17-AUG-2021 23:41:58 17-AUG-2021 23:41:58
AQ_ADMINISTRATOR_ROLE 17-AUG-2021 23:42:09

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
AQ_USER_ROLE 17-AUG-2021 23:42:10
DATAPUMP_EXP_FULL_DATABASE 17-AUG-2021 23:42:12
DATAPUMP_IMP_FULL_DATABASE 17-AUG-2021 23:42:12
ADM_PARALLEL_EXECUTE_TASK 17-AUG-2021 23:42:27
PROVISIONER 17-AUG-2021 23:42:30
XS_SESSION_ADMIN 17-AUG-2021 23:42:30
XS_NAMESPACE_ADMIN 17-AUG-2021 23:42:30
XS_CACHE_ADMIN 17-AUG-2021 23:42:30
XS_CONNECT 17-AUG-2021 23:42:30
XS$NULL 17-AUG-2021 23:42:31 17-AUG-2021 23:42:31
HS_ADMIN_EXECUTE_ROLE 17-AUG-2021 23:56:55

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
DBSFWUSER 17-AUG-2021 23:42:38 20-JAN-2022 19:26:01
GATHER_SYSTEM_STATISTICS 17-AUG-2021 23:44:25
OPTIMIZER_PROCESSING_RATE 17-AUG-2021 23:44:25
DBMS_MDX_INTERNAL 17-AUG-2021 23:44:38
ORACLE_OCM 17-AUG-2021 23:44:51 20-JAN-2022 19:26:01
BDSQL_ADMIN 17-AUG-2021 23:45:09
BDSQL_USER 17-AUG-2021 23:45:09
RECOVERY_CATALOG_OWNER 17-AUG-2021 23:45:24
RECOVERY_CATALOG_OWNER_VPD 17-AUG-2021 23:45:24
RECOVERY_CATALOG_USER 17-AUG-2021 23:45:24
EM_EXPRESS_BASIC 17-AUG-2021 23:51:01

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
EM_EXPRESS_ALL 17-AUG-2021 23:51:01
SYSUMF_ROLE 17-AUG-2021 23:54:11
SYS$UMF 17-AUG-2021 23:54:11 20-JAN-2022 19:26:01
MAINTPLAN_APP 17-AUG-2021 23:54:12
SCHEDULER_ADMIN 17-AUG-2021 23:56:14
PPLB_ROLE 17-AUG-2021 23:56:33
DGPDB_INT 17-AUG-2021 23:56:34 20-JAN-2022 19:26:01
HS_ADMIN_SELECT_ROLE 17-AUG-2021 23:56:55
SODA_APP 18-AUG-2021 00:08:17
HS_ADMIN_ROLE 17-AUG-2021 23:56:55
GLOBAL_AQ_USER_ROLE 17-AUG-2021 23:56:58

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
OEM_ADVISOR 17-AUG-2021 23:59:19
OEM_MONITOR 17-AUG-2021 23:59:19
DBSNMP 17-AUG-2021 23:59:19 20-JAN-2022 19:26:01
APPQOSSYS 17-AUG-2021 23:59:22 20-JAN-2022 19:26:01
GSMADMIN_ROLE 17-AUG-2021 23:59:23
GSM_POOLADMIN_ROLE 17-AUG-2021 23:59:23
GDS_CATALOG_SELECT 17-AUG-2021 23:59:24
GSMCATUSER 17-AUG-2021 23:59:24 20-JAN-2022 19:26:01
GGSYS 17-AUG-2021 23:59:33 20-JAN-2022 19:26:01
GGSYS_ROLE 17-AUG-2021 23:59:34
XDB 18-AUG-2021 00:01:47 20-JAN-2022 19:26:01

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
ANONYMOUS 18-AUG-2021 00:01:47 20-JAN-2022 19:26:01
XDBADMIN 18-AUG-2021 00:01:47
XDB_SET_INVOKER 18-AUG-2021 00:02:13
AUTHENTICATEDUSER 18-AUG-2021 00:02:16
XDB_WEBSERVICES 18-AUG-2021 00:02:16
XDB_WEBSERVICES_WITH_PUBLIC 18-AUG-2021 00:02:16
XDB_WEBSERVICES_OVER_HTTP 18-AUG-2021 00:02:16
OLAPSYS 18-AUG-2021 00:19:25 20-JAN-2022 19:26:01
DATAPATCH_ROLE 18-AUG-2021 00:08:26
WMSYS 18-AUG-2021 00:09:33 20-JAN-2022 19:26:01
WM_ADMIN_ROLE 18-AUG-2021 00:09:39

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
JAVAUSERPRIV 18-AUG-2021 00:11:53
JAVAIDPRIV 18-AUG-2021 00:11:53
JAVASYSPRIV 18-AUG-2021 00:11:53
JAVADEBUGPRIV 18-AUG-2021 00:11:53
EJBCLIENT 18-AUG-2021 00:11:53
JMXSERVER 18-AUG-2021 00:11:53
DBJAVASCRIPT 18-AUG-2021 00:11:53
OJVMSYS 18-AUG-2021 00:11:54 20-JAN-2022 19:26:01
JAVA_ADMIN 18-AUG-2021 00:12:19
CTXSYS 18-AUG-2021 00:15:16 20-JAN-2022 19:26:01
CTXAPP 18-AUG-2021 00:15:19

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
ORDSYS 18-AUG-2021 00:16:17 20-JAN-2022 19:26:01
ORDDATA 18-AUG-2021 00:16:17 20-JAN-2022 19:26:01
ORDPLUGINS 18-AUG-2021 00:16:17 20-JAN-2022 19:26:01
SI_INFORMTN_SCHEMA 18-AUG-2021 00:16:17 20-JAN-2022 19:26:01
ORDADMIN 18-AUG-2021 00:17:38
OLAP_XS_ADMIN 18-AUG-2021 00:19:20
DVSYS 18-AUG-2021 00:31:50 20-JAN-2022 19:26:01
DV_SECANALYST 18-AUG-2021 00:32:01
OLAP_DBA 18-AUG-2021 00:19:27
OLAP_USER 18-AUG-2021 00:19:27
MDSYS 18-AUG-2021 00:25:52 20-JAN-2022 19:26:01

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
MDDATA 18-AUG-2021 00:25:52 20-JAN-2022 19:26:01
RDFCTX_ADMIN 18-AUG-2021 00:29:43
LBACSYS 18-AUG-2021 00:30:51 20-JAN-2022 19:26:01
LBAC_DBA 18-AUG-2021 00:30:52
DVF 18-AUG-2021 00:31:50 20-JAN-2022 19:26:01
DV_MONITOR 18-AUG-2021 00:32:01
DV_ADMIN 18-AUG-2021 00:32:01
DV_OWNER 18-AUG-2021 00:32:01
DV_ACCTMGR 18-AUG-2021 00:32:01
DV_PATCH_ADMIN 18-AUG-2021 00:32:01
DV_STREAMS_ADMIN 18-AUG-2021 00:32:01

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
DV_GOLDENGATE_ADMIN 18-AUG-2021 00:32:01
DV_XSTREAM_ADMIN 18-AUG-2021 00:32:01
DV_GOLDENGATE_REDO_ACCESS 18-AUG-2021 00:32:01
DV_AUDIT_CLEANUP 18-AUG-2021 00:32:01
PDBADMIN 20-JAN-2022 19:26:00 20-JAN-2022 19:26:00 19-JUL-2022 19:26:00 27-JAN-2023 20:59:47
DV_DATAPUMP_NETWORK_LINK 18-AUG-2021 00:32:01
DV_POLICY_OWNER 18-AUG-2021 00:32:01
PFCL_VD 13-JAN-2023 10:11:06 13-JAN-2023 10:11:06 12-JUL-2023 10:11:06 13-MAR-2025 10:08:47 13-JAN-2023 10:11:07
PFCL_VP 13-JAN-2023 10:11:06 13-JAN-2023 10:11:06 12-JUL-2023 10:11:06 13-MAR-2025 10:08:47
APP_ROLE 27-JAN-2023 13:04:43
AA 27-JAN-2023 13:04:56 27-SEP-2023 16:10:39 18-MAR-2025 10:11:03 11-MAR-2025 10:11:03

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
BB 27-JAN-2023 13:05:15 27-JAN-2023 13:05:15 26-JUL-2023 13:05:15 13-MAR-2025 10:08:47
ORASCAN 27-JAN-2023 13:35:42 27-JAN-2023 13:35:42 17-MAR-2025 11:13:01 13-MAR-2025 16:29:11
PETE1 05-MAY-2023 09:13:00
PETE2 05-MAY-2023 09:13:05
PETE3 05-MAY-2023 09:13:10
TESTTEST 14-JUL-2023 09:59:52 14-JUL-2023 09:59:52 10-JAN-2024 09:59:52 13-MAR-2025 10:08:47
XXA 08-AUG-2023 15:06:52 08-AUG-2023 15:06:52 04-FEB-2024 15:06:52 13-MAR-2025 10:08:47 08-AUG-2023 14:12:16
XXB 08-AUG-2023 15:08:20 08-AUG-2023 15:08:20 04-FEB-2024 15:08:20 13-MAR-2025 10:08:47
USER03 06-MAR-2025 14:10:25 06-MAR-2025 14:10:25 02-SEP-2025 14:10:25
U1 12-SEP-2023 10:01:41 12-SEP-2023 10:02:44 10-MAR-2024 10:02:44 13-MAR-2025 10:08:47 12-SEP-2023 09:03:49
UU 27-SEP-2023 19:09:24 27-SEP-2023 19:09:24 25-MAR-2024 19:09:24 13-MAR-2025 10:08:47 27-SEP-2023 18:11:10

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
DEV2 27-SEP-2023 20:17:53 27-SEP-2023 20:17:53 25-MAR-2024 20:17:53 13-MAR-2025 10:08:47
ORABLOGDBA 27-SEP-2023 20:00:46 27-SEP-2023 20:00:46 25-MAR-2024 20:00:46 13-MAR-2025 10:08:47 27-SEP-2023 19:00:54
ERIC 06-MAR-2025 14:10:18 06-MAR-2025 14:10:18 02-SEP-2025 14:10:18
EMIL 06-MAR-2025 14:10:18 06-MAR-2025 14:10:18 02-SEP-2025 14:10:18
ZULIA 06-MAR-2025 14:10:18 06-MAR-2025 14:10:18 02-SEP-2025 14:10:18
PETE 06-MAR-2025 14:10:18 06-MAR-2025 14:10:18 02-SEP-2025 14:10:18
FRED 06-MAR-2025 14:10:18 06-MAR-2025 14:10:18 02-SEP-2025 14:10:18
BILL 06-MAR-2025 14:10:18 06-MAR-2025 14:10:18 02-SEP-2025 14:10:18
JIM 06-MAR-2025 14:10:18 06-MAR-2025 14:10:18 02-SEP-2025 14:10:18
IMPORTER 06-MAR-2025 14:10:18 06-MAR-2025 14:10:18 02-SEP-2025 14:10:18 06-MAR-2025 14:10:23
ORABLOG_ADMIN 06-MAR-2025 14:10:18

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
ORABLOG_READ 06-MAR-2025 14:10:24
ORABLOG_CREDIT 06-MAR-2025 14:10:24
ORABLOG_SUPPORT 06-MAR-2025 14:10:24
USER01 06-MAR-2025 14:10:24 06-MAR-2025 14:10:24 02-SEP-2025 14:10:24
USER02 06-MAR-2025 14:10:25 06-MAR-2025 14:10:25 02-SEP-2025 14:10:25
FACADM 21-JAN-2022 19:01:55 10-SEP-2023 13:40:08 08-MAR-2024 13:40:08 13-MAR-2025 10:08:47 27-SEP-2023 18:12:54
SCH 22-JAN-2022 23:15:50 22-JAN-2022 23:15:50 21-JUL-2022 23:15:50 27-JAN-2023 20:59:47 22-JAN-2022 23:17:21
USE 22-JAN-2022 23:20:18 22-JAN-2022 23:20:18 21-JUL-2022 23:20:18 27-JAN-2023 20:59:47 23-JAN-2022 01:33:18
_NEXT_USER 17-AUG-2021 23:05:41
DV_CONNECT2 24-JUN-2026 13:17:06 24-JUN-2026 13:17:06 21-DEC-2026 13:17:06 24-JUN-2026 12:21:07
C##DVO 24-JUN-2026 13:17:24 24-JUN-2026 14:26:10

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
C##DVO_BK 24-JUN-2026 13:17:24 24-JUN-2026 12:20:56
C##ACCO 24-JUN-2026 13:17:24 24-JUN-2026 14:25:51
C##ACCO_BK 24-JUN-2026 13:17:24 24-JUN-2026 12:20:56
SECURITY 24-JUN-2026 13:21:28 24-JUN-2026 13:21:28 21-DEC-2026 13:21:28
SEC_AUDITOR 24-JUN-2026 13:21:59 24-JUN-2026 13:21:59 21-DEC-2026 13:21:59 25-JUN-2026 06:59:44
ORABLOG 21-JAN-2022 17:48:28 24-JUN-2026 13:17:06 21-DEC-2026 13:17:06 24-JUN-2026 12:21:30
USER04 06-MAR-2025 14:10:25 06-MAR-2025 14:10:25 02-SEP-2025 14:10:25
USER05 06-MAR-2025 14:10:25 06-MAR-2025 14:10:25 02-SEP-2025 14:10:25
USER06 06-MAR-2025 14:10:26 06-MAR-2025 14:10:26 02-SEP-2025 14:10:26
USER07 06-MAR-2025 14:10:26 06-MAR-2025 14:10:26 02-SEP-2025 14:10:26
BACK01 06-MAR-2025 14:10:26 06-MAR-2025 14:10:26 02-SEP-2025 14:10:26

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
BATCH01 06-MAR-2025 14:10:26 06-MAR-2025 14:10:26 02-SEP-2025 14:10:26
FEED01 06-MAR-2025 14:10:26 06-MAR-2025 14:10:26 02-SEP-2025 14:10:26
DEV01 06-MAR-2025 14:10:26 06-MAR-2025 14:10:26 02-SEP-2025 14:10:26
DEV02 06-MAR-2025 14:10:26 06-MAR-2025 14:10:26 02-SEP-2025 14:10:26
DEV03 06-MAR-2025 14:10:27 06-MAR-2025 14:10:27 02-SEP-2025 14:10:27
RISK01 06-MAR-2025 14:10:27 06-MAR-2025 14:10:27 02-SEP-2025 14:10:27
DEV 12-MAR-2025 16:46:00 12-MAR-2025 16:46:00 08-SEP-2025 16:46:00 12-MAR-2025 16:46:07
DBAUSER 11-MAR-2025 10:04:09 11-MAR-2025 10:04:09 07-SEP-2025 10:04:09 11-MAR-2025 10:04:12
VU 13-MAR-2025 13:18:36 13-MAR-2025 13:18:36 09-SEP-2025 13:18:36 13-MAR-2025 13:20:31
VA 13-MAR-2025 13:18:42 13-MAR-2025 13:18:42 09-SEP-2025 13:18:42 13-MAR-2025 13:19:46
VB 13-MAR-2025 13:19:55 13-MAR-2025 13:19:55 09-SEP-2025 13:19:55 13-MAR-2025 13:19:59

NAME CTIME PTIME EXPTIME LTIME SPARE6 SPARE11
------------------------------ -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
CCKEY 13-MAR-2025 13:21:29 13-MAR-2025 13:21:29 09-SEP-2025 13:21:29 13-MAR-2025 13:22:28
PWDP 13-MAR-2025 13:25:47 13-MAR-2025 13:26:06 09-SEP-2025 13:26:06
DV_CONNECT 24-JUN-2026 13:17:06 24-JUN-2026 13:17:06 21-DEC-2026 13:17:06 24-JUN-2026 12:21:16
SCOTT 09-APR-2025 15:48:25 09-APR-2025 15:48:25 06-OCT-2025 15:48:25

191 rows selected.

SQL>

I was looking at all of the columns in USER$ covering the CTIME which records the timestamp that the user was created and PTIME which shows the timestamp of the last password change, EXPTIME which shows when the accounts password was expired and LTIME which shows when the account was locked. Of course apart from CTIME the other columns are not obvious.

Spare6 has some entries in my 21c database. But what is it used for:

SQL> alter session set container=cdb$root;

Session altered.

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

TEXT
--------------------------------------------------------------------------------
select u.name, u.user#,
decode(u.password, 'GLOBAL', u.password,
'EXTERNAL', u.password,
NULL),
m.status,
decode(mod(u.astatus, 16), 4, u.ltime,
5, u.ltime,
6, u.ltime,
8, u.ltime,
9, u.ltime,
10, u.ltime, to_date(NULL)),

TEXT
--------------------------------------------------------------------------------
decode(mod(u.astatus, 16),
1, u.exptime,
2, u.exptime,
5, u.exptime,
6, u.exptime,
9, u.exptime,
10, u.exptime,
decode(bitand(u.spare1,65536), 65536, to_date(NULL),
decode(u.password, 'GLOBAL', to_date(NULL),
'EXTERNAL', to_date(NULL),
decode(u.ptime, '', to_date(NULL),

TEXT
--------------------------------------------------------------------------------
decode(pr.limit#, 2147483647, to_date(NULL),
decode(pr.limit#, 0,
decode(dp.limit#, 2147483647, to_date(NULL), u.ptime +
dp.limit#/86400),
u.ptime + pr.limit#/86400)))))),
dts.name, tts.name, ltts.name,
u.ctime, p.name,
nvl(cgm.consumer_group, 'DEFAULT_CONSUMER_GROUP'),
u.ext_username,
decode(bitand(u.spare1, 65536), 65536, NULL, decode(
REGEXP_INSTR(

TEXT
--------------------------------------------------------------------------------
NVL2(u.password, u.password, ' '),
'^ $'
),
0,
decode(length(u.password), 16, '10G ', NULL),
''
) ||
decode(
REGEXP_INSTR(
REGEXP_REPLACE(
NVL2(u.spare4, u.spare4, ' '),

TEXT
--------------------------------------------------------------------------------
'S:000000000000000000000000000000000000000000000000000000000000',
'not_a_verifier'
),
'S:'
),
0, '', '11G '
) ||
decode(
REGEXP_INSTR(
NVL2(u.spare4, u.spare4, ' '),
'T:'

TEXT
--------------------------------------------------------------------------------
),
0, '', '12C '
) ||
decode(
REGEXP_INSTR(
REGEXP_REPLACE(
NVL2(u.spare4, u.spare4, ' '),
'H:00000000000000000000000000000000',
'not_a_verifier'
),
'H:'

TEXT
--------------------------------------------------------------------------------
),
0, '', 'HTTP '
)),
decode(bitand(u.spare1, 16),
16, 'Y',
'N'),
decode(bitand(u.spare1,65536), 65536, 'NONE',
decode(u.password, 'GLOBAL', 'GLOBAL',
'EXTERNAL', 'EXTERNAL',
'PASSWORD')),
decode(bitand(u.spare1, 10272),

TEXT
--------------------------------------------------------------------------------
32, 'Y', 2048, 'Y', 2080, 'Y',
8192, 'Y', 8224, 'Y', 10240, 'Y',
10272, 'Y',
'N'),
decode(bitand(u.spare1, 128), 0, 'NO', 'YES'),
from_tz(to_timestamp(to_char(u.spare6, 'DD-MON-YYYY HH24:MI:SS'),
'DD-MON-YYYY HH24:MI:SS'), '0:00')
at time zone sessiontimezone,
decode(bitand(u.spare1, 256), 256, 'Y', 'N'),
decode(bitand(u.spare1, 4224),
128, decode(SYS_CONTEXT('USERENV', 'CON_ID'), 1, 'NO', 'YES'),

TEXT
--------------------------------------------------------------------------------
4224, decode(SYS_CONTEXT('USERENV', 'IS_APPLICATION_PDB'),
'YES', 'YES', 'NO'),
'NO'),
nls_collation_name(nvl(u.spare3, 16382)),
-- IMPLICIT
decode(bitand(u.spare1, 32768), 32768, 'YES', 'NO'),
-- ALL_SHARD
decode(bitand(u.spare1, 16384), 16384, 'YES', 'NO'),
-- EXTERNAL_SHARD
decode(bitand(u.spare1, 262144), 262144, 'YES', 'NO'),
-- PASSWORD_CHANGE_DATE

TEXT
--------------------------------------------------------------------------------
u.ptime,
-- MANDATORY_PROFILE_VIOLATION
decode(bitand(u.astatus, 64), 64, 'YES', 'NO')
from sys.user$ u
left outer join sys.resource_group_mapping$ cgm
on (cgm.attribute = 'ORACLE_USER' and cgm.status = 'ACTIVE' and
cgm.value = u.name) left outer join sys.ts$ ltts
on (u.spare9 = ltts.ts#),
sys.ts$ dts, sys.ts$ tts, sys.profname$ p,
sys.user_astatus_map m, sys.profile$ pr, sys.profile$ dp
where u.datats# = dts.ts#

TEXT
--------------------------------------------------------------------------------
and u.resource$ = p.profile#
and u.tempts# = tts.ts#
and ((u.astatus = m.status#) or
(u.astatus = (m.status# + 16 - BITAND(m.status#, 16))) or
(u.astatus = (m.status# + 64 - BITAND(m.status#, 64))))
and u.type# = 1
and u.resource$ = pr.profile#
and dp.profile# = 0
and dp.type#=1
and dp.resource#=1
and pr.type# = 1

TEXT
--------------------------------------------------------------------------------
and pr.resource# = 1


SQL>

From the source code of the view we can work out that user$.spare6 is the LAST_LOGIN time and we can check this via the view:

SQL> col username for a30
SQL> col last_login for a40
SQL> select username,to_char(last_login,'DD-MON-YYYY HH24:MI:SS') last_login from dba_users where last_login is not null;

USERNAME LAST_LOGIN
------------------------------ ----------------------------------------
SYSTEM 28-APR-2025 09:33:47
AA 11-MAR-2025 11:11:03
XXA 08-AUG-2023 15:12:16
VU 13-MAR-2025 14:20:31
C##ACCO 24-JUN-2026 15:25:51
USE 23-JAN-2022 02:33:18
DBAUSER 11-MAR-2025 11:04:12
U1 12-SEP-2023 10:03:49
SCH 23-JAN-2022 00:17:21
IMPORTER 06-MAR-2025 15:10:23
C##DVO 24-JUN-2026 15:26:10

USERNAME LAST_LOGIN
------------------------------ ----------------------------------------
SEC_AUDITOR 25-JUN-2026 07:59:44
DV_CONNECT 24-JUN-2026 13:21:16
FACADM 27-SEP-2023 19:12:54
DEV 12-MAR-2025 17:46:07
VA 13-MAR-2025 14:19:46
DV_CONNECT2 24-JUN-2026 13:21:07
ORABLOGDBA 27-SEP-2023 20:00:54
ORABLOG 24-JUN-2026 13:21:30
VB 13-MAR-2025 14:19:59
PFCL_VD 13-JAN-2023 11:11:07
C##ACCO_BK 24-JUN-2026 13:20:56

USERNAME LAST_LOGIN
------------------------------ ----------------------------------------
ORASCAN 13-MAR-2025 17:29:11
UU 27-SEP-2023 19:11:10
CCKEY 13-MAR-2025 14:22:28
C##DVO_BK 24-JUN-2026 13:20:56

26 rows selected.

SQL>

That looks correct but the view does not use spare 11 so what is it used for? If we look at dcore.bsq where user$ is created we see:

/* also as base schema name for adjunct schemas */
spare1 number, /* used for schema level supp. logging: see ktscts.h */
/* 0x80 - 128 (CDB common users) */
/* 0x100 - 256 (Oracle maintained user) */
/* 0x1000 - 4096 (For Application common users, both
COMMON(128) and APPCMN(4096) are set) */
/* 0x8000 - 32768 (Implicit Application common users) */
/* 0x10000 - 65536 (NO authentication user) */
/* 0x80000 - 524288 (Protected user) */
/* 0x200000 - 2097152 (User was created as PDB Admin at the
time of PDB creation) */
/* spare2 is used to store */
/* - edition id for adjunct schemas (type# = 2) */
/* - base schema id for schema synonyms (type# = 3) */
spare2 number,
spare3 number, /* used for schema-level default collation */
spare4 varchar2(1000),
spare5 varchar2(1000),
spare6 date, /* used for Last Successful Logon Time */
spare7 varchar2(4000),
spare8 varchar2(4000),
spare9 number, /* default local temporaty tablespace */
spare10 number, /* Creation Application ID */
spare11 timestamp
)

SPARE11 does not have any comment against it and a grep of the .sql files does not show any use of the column. We have to assume that SPARE11 is not used at this point and in the version I am looking at here which is 21c.

Any date/timestamp is useful for forensics as it places any action on a timeline

#oracleace #sym_42 #oracle #database #security #forensics #timeline