First you have to define which system privileges are critical for you. For example the below list is enough for me. Normal App user should not have below priviliges.
Now The simplest query to find who has the system privileges. But the output of this query can be devious.
Because ; Users and Roles can be seen at the query result.
SELECT *
FROM DBA_SYS_PRIVS
WHERE PRIVILEGE IN
('BECOME USER',
'ALTER USER',
'DROP USER',
'CREATE ROLE',
'ALTER ANY ROLE',
'DROP ANY ROLE',
'GRANT ANY ROLE',
'CREATE PROFILE',
'ALTER PROFILE',
'DROP PROFILE',
'CREATE ANY TABLE',
'ALTER ANY TABLE',
'DROP ANY TABLE',
'INSERT ANY TABLE',
'UPDATE ANY TABLE',
'DELETE ANY TABLE',
'CREATE ANY PROCEDURE',
'ALTER ANY PROCEDURE',
'DROP ANY PROCEDURE',
'CREATE ANY TRIGGER',
'ALTER ANY TRIGGER',
'DROP ANY TRIGGER',
'CREATE TABLESPACE',
'ALTER TABLESPACE',
'DROP TABLESPACES',
'ALTER DATABASE',
'ALTER SYSTEM');
In order to report only Oracle user ; we should add a simple predicate .
SELECT *
FROM DBA_SYS_PRIVS
WHERE PRIVILEGE IN
('BECOME USER',
'ALTER USER',
'DROP USER',
'CREATE ROLE',
'ALTER ANY ROLE',
'DROP ANY ROLE',
'GRANT ANY ROLE',
'CREATE PROFILE',
'ALTER PROFILE',
'DROP PROFILE',
'CREATE ANY TABLE',
'ALTER ANY TABLE',
'DROP ANY TABLE',
'INSERT ANY TABLE',
'UPDATE ANY TABLE',
'DELETE ANY TABLE',
'CREATE ANY PROCEDURE',
'ALTER ANY PROCEDURE',
'DROP ANY PROCEDURE',
'CREATE ANY TRIGGER',
'ALTER ANY TRIGGER',
'DROP ANY TRIGGER',
'CREATE TABLESPACE',
'ALTER TABLESPACE',
'DROP TABLESPACES',
'ALTER DATABASE',
'ALTER SYSTEM')
AND grantee IN (SELECT username FROM dba_users);
Now the point is ; some Oracle users normally have SYSTEM PRIVILEGES; therefore SYSTEM , SYS and some oracle users should not be on the query output.
SELECT *
FROM DBA_SYS_PRIVS
WHERE PRIVILEGE IN
('BECOME USER',
'ALTER USER',
'DROP USER',
'CREATE ROLE',
'ALTER ANY ROLE',
'DROP ANY ROLE',
'GRANT ANY ROLE',
'CREATE PROFILE',
'ALTER PROFILE',
'DROP PROFILE',
'CREATE ANY TABLE',
'ALTER ANY TABLE',
'DROP ANY TABLE',
'INSERT ANY TABLE',
'UPDATE ANY TABLE',
'DELETE ANY TABLE',
'CREATE ANY PROCEDURE',
'ALTER ANY PROCEDURE',
'DROP ANY PROCEDURE',
'CREATE ANY TRIGGER',
'ALTER ANY TRIGGER',
'DROP ANY TRIGGER',
'CREATE TABLESPACE',
'ALTER TABLESPACE',
'DROP TABLESPACES',
'ALTER DATABASE',
'ALTER SYSTEM')
AND grantee IN
(SELECT username
FROM dba_users
WHERE username NOT IN
('PGAADMIN',
'WMSYS',
'LBACSYS',
'SYS',
'SYSTEM',
'SGYON_KEY_USR',
'EXFSYS',
'DVOWNER',
'DVACCTMNGR',
'DVSYS',
'DWOWNER',
'RMAN', 'GGATE'));
Now we can list the Oracle users who have the critical system privileges. Is that enough ? It is not enough ;
because we can create a role and we assign this role to any user. After that we can create another role and we can grant this role the previous create role and at the end we can grant a critical system privilege to this role . If you run this query you can't list this user. Because the nature of recursive roles hides this information.
To solve this problem we should use hierarchical query START WITH and CONNECT BY clauses.
And the result ;
SELECT DISTINCT c "User Names"
FROM ( /* THE PRIVILEGES */
SELECT NULL p, name c
FROM system_privilege_map
WHERE name IN
('BECOME USER',
'ALTER USER',
'DROP USER',
'CREATE ROLE',
'ALTER ANY ROLE',
'DROP ANY ROLE',
'GRANT ANY ROLE',
'CREATE PROFILE',
'ALTER PROFILE',
'DROP PROFILE',
'CREATE ANY TABLE',
'ALTER ANY TABLE',
'DROP ANY TABLE',
'INSERT ANY TABLE',
'UPDATE ANY TABLE',
'DELETE ANY TABLE',
'CREATE ANY PROCEDURE',
'ALTER ANY PROCEDURE',
'DROP ANY PROCEDURE',
'CREATE ANY TRIGGER',
'ALTER ANY TRIGGER',
'DROP ANY TRIGGER',
'CREATE TABLESPACE',
'ALTER TABLESPACE',
'DROP TABLESPACES',
'ALTER DATABASE',
'ALTER SYSTEM')
/* ROLE RELATIONS */
UNION
SELECT granted_role p, grantee c FROM dba_role_privs
/* PRIVILEGE RELATIONS */
UNION
SELECT privilege p, grantee c FROM dba_sys_privs)
WHERE c IN
(SELECT username
FROM dba_users
WHERE username NOT IN
('PGAADMIN',
'WMSYS',
'LBACSYS',
'SYS',
'SYSTEM',
'SGYON_KEY_USR',
'EXFSYS',
'DVOWNER',
'DVACCTMNGR',
'DVSYS',
'DWOWNER',
'RMAN',
'GGATE'))
START WITH p IS NULL
CONNECT BY p = PRIOR c;